| // Copyright 2021 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/style/icon_button.h" |
| |
| #include <variant> |
| |
| #include "ash/public/cpp/style/color_provider.h" |
| #include "ash/style/blurred_background_shield.h" |
| #include "ash/style/style_util.h" |
| #include "ash/style/text_image.h" |
| #include "base/notreached.h" |
| #include "base/strings/utf_string_conversion_utils.h" |
| #include "chromeos/utils/haptics_util.h" |
| #include "ui/accessibility/ax_node_data.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/color/color_variant.h" |
| #include "ui/events/devices/haptic_touchpad_effects.h" |
| #include "ui/events/event.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/geometry/rounded_corners_f.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/gfx/image/image_skia_operations.h" |
| #include "ui/gfx/paint_vector_icon.h" |
| #include "ui/gfx/vector_icon_types.h" |
| #include "ui/views/accessibility/view_accessibility.h" |
| #include "ui/views/background.h" |
| #include "ui/views/border.h" |
| #include "ui/views/controls/highlight_path_generator.h" |
| |
| namespace ash { |
| namespace { |
| |
| constexpr int kXSmallButtonSize = 20; |
| constexpr int kSmallButtonSize = 24; |
| constexpr int kMediumButtonSize = 32; |
| constexpr int kLargeButtonSize = 36; |
| constexpr int kXLargeButtonSize = 48; |
| |
| // Icon size of the small, medium and large size buttons. |
| constexpr int kIconSize = 20; |
| // Icon size of the extra small size button. |
| constexpr int kXSmallIconSize = 16; |
| |
| // The gap between the focus ring and the button's content. |
| constexpr int kFocusRingPadding = 2; |
| |
| // The default toggled background and icon color IDs. |
| constexpr ui::ColorId kDefaultToggledBackgroundColorId = |
| cros_tokens::kCrosSysSystemPrimaryContainer; |
| constexpr ui::ColorId kDefaultToggledIconColorId = |
| cros_tokens::kCrosSysSystemOnPrimaryContainer; |
| |
| int GetButtonSizeOnType(IconButton::Type type) { |
| switch (type) { |
| case IconButton::Type::kXSmall: |
| case IconButton::Type::kXSmallProminent: |
| case IconButton::Type::kXSmallFloating: |
| case IconButton::Type::kXSmallProminentFloating: |
| return kXSmallButtonSize; |
| case IconButton::Type::kSmall: |
| case IconButton::Type::kSmallProminent: |
| case IconButton::Type::kSmallFloating: |
| case IconButton::Type::kSmallProminentFloating: |
| return kSmallButtonSize; |
| case IconButton::Type::kMedium: |
| case IconButton::Type::kMediumProminent: |
| case IconButton::Type::kMediumFloating: |
| case IconButton::Type::kMediumProminentFloating: |
| return kMediumButtonSize; |
| case IconButton::Type::kLarge: |
| case IconButton::Type::kLargeProminent: |
| case IconButton::Type::kLargeFloating: |
| case IconButton::Type::kLargeProminentFloating: |
| return kLargeButtonSize; |
| case IconButton::Type::kXLarge: |
| case IconButton::Type::kXLargeProminent: |
| case IconButton::Type::kXLargeFloating: |
| case IconButton::Type::kXLargeProminentFloating: |
| return kXLargeButtonSize; |
| } |
| } |
| |
| std::optional<ui::ColorId> GetDefaultBackgroundColorId(IconButton::Type type) { |
| switch (type) { |
| case IconButton::Type::kXSmall: |
| case IconButton::Type::kSmall: |
| case IconButton::Type::kMedium: |
| case IconButton::Type::kLarge: |
| case IconButton::Type::kXLarge: |
| return cros_tokens::kCrosSysSystemOnBase; |
| case IconButton::Type::kXSmallProminent: |
| case IconButton::Type::kSmallProminent: |
| case IconButton::Type::kMediumProminent: |
| case IconButton::Type::kLargeProminent: |
| case IconButton::Type::kXLargeProminent: |
| return cros_tokens::kCrosSysSystemPrimaryContainer; |
| default: |
| NOTREACHED() << "Floating type button does not have a background"; |
| } |
| } |
| |
| ui::ColorId GetDefaultIconColorId(IconButton::Type type, bool focused) { |
| switch (type) { |
| case IconButton::Type::kXSmall: |
| case IconButton::Type::kXSmallFloating: |
| case IconButton::Type::kSmall: |
| case IconButton::Type::kSmallFloating: |
| case IconButton::Type::kMedium: |
| case IconButton::Type::kMediumFloating: |
| case IconButton::Type::kLarge: |
| case IconButton::Type::kLargeFloating: |
| case IconButton::Type::kXLarge: |
| case IconButton::Type::kXLargeFloating: |
| return cros_tokens::kCrosSysOnSurface; |
| case IconButton::Type::kXSmallProminent: |
| case IconButton::Type::kSmallProminent: |
| case IconButton::Type::kMediumProminent: |
| case IconButton::Type::kLargeProminent: |
| case IconButton::Type::kXLargeProminent: |
| return cros_tokens::kCrosSysSystemOnPrimaryContainer; |
| case IconButton::Type::kXSmallProminentFloating: |
| case IconButton::Type::kSmallProminentFloating: |
| case IconButton::Type::kMediumProminentFloating: |
| case IconButton::Type::kLargeProminentFloating: |
| case IconButton::Type::kXLargeProminentFloating: |
| return focused ? cros_tokens::kCrosSysPrimary |
| : cros_tokens::kCrosSysSecondary; |
| } |
| } |
| |
| int GetIconSizeOnType(IconButton::Type type) { |
| if (type == IconButton::Type::kXSmall || |
| type == IconButton::Type::kXSmallFloating || |
| type == IconButton::Type::kXSmallProminent || |
| type == IconButton::Type::kXSmallProminentFloating) { |
| return kXSmallIconSize; |
| } |
| return kIconSize; |
| } |
| |
| bool IsFloatingIconButton(IconButton::Type type) { |
| switch (type) { |
| case IconButton::Type::kXSmallFloating: |
| case IconButton::Type::kXSmallProminentFloating: |
| case IconButton::Type::kSmallFloating: |
| case IconButton::Type::kSmallProminentFloating: |
| case IconButton::Type::kMediumFloating: |
| case IconButton::Type::kMediumProminentFloating: |
| case IconButton::Type::kLargeFloating: |
| case IconButton::Type::kLargeProminentFloating: |
| case IconButton::Type::kXLargeFloating: |
| case IconButton::Type::kXLargeProminentFloating: |
| return true; |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| bool IsProminentFloatingType(IconButton::Type type) { |
| switch (type) { |
| case IconButton::Type::kXSmallProminentFloating: |
| case IconButton::Type::kSmallProminentFloating: |
| case IconButton::Type::kMediumProminentFloating: |
| case IconButton::Type::kLargeProminentFloating: |
| case IconButton::Type::kXLargeProminentFloating: |
| return true; |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| // Create a themed fully rounded rect background for icon button. |
| std::unique_ptr<views::Background> CreateThemedBackground( |
| ui::ColorId color_id, |
| IconButton::Type type) { |
| return views::CreateRoundedRectBackground(color_id, |
| GetButtonSizeOnType(type) / 2); |
| } |
| |
| // Create a solid color fully rounded rect background for icon button. |
| std::unique_ptr<views::Background> CreateSolidBackground( |
| ui::ColorVariant color, |
| IconButton::Type type) { |
| return views::CreateRoundedRectBackground( |
| color, gfx::RoundedCornersF(GetButtonSizeOnType(type) / 2)); |
| } |
| |
| // Returns a normal and disabled image model for `symbol`. `is_toggled` and |
| // `color_id` control styling. The returned model dimensions wil be `icon_size` |
| // x `icon_size` in pixels. |
| std::pair<ui::ImageModel, ui::ImageModel> SymbolImages( |
| const bool is_toggled, |
| ui::ColorId color_id, |
| const int icon_size, |
| base_icu::UChar32 symbol) { |
| const gfx::Size size(icon_size, icon_size); |
| ui::ImageModel normal_model = TextImage::AsImageModel(size, symbol, color_id); |
| ui::ImageModel disabled_model = |
| TextImage::AsImageModel(size, symbol, cros_tokens::kCrosSysDisabled); |
| |
| return {normal_model, disabled_model}; |
| } |
| |
| } // namespace |
| |
| IconButton::Builder::Builder() |
| : type_(IconButton::Type::kSmall), |
| icon_(nullptr), |
| accessible_name_(u""), |
| is_togglable_(false), |
| has_border_(false) {} |
| |
| IconButton::Builder::~Builder() = default; |
| |
| std::unique_ptr<IconButton> IconButton::Builder::Build() { |
| if (!character_) { |
| // `icon_` must be non-null if there is no character. |
| CHECK(icon_); |
| } |
| |
| std::u16string accessible_name; |
| if (std::holds_alternative<int>(accessible_name_)) { |
| accessible_name = |
| l10n_util::GetStringUTF16(std::get<int>(accessible_name_)); |
| } else { |
| accessible_name = std::get<std::u16string>(accessible_name_); |
| } |
| |
| auto button = std::make_unique<IconButton>( |
| std::move(callback_), type_, icon_, accessible_name, |
| /*is_togglable=*/is_togglable_, /*has_border=*/has_border_); |
| if (view_id_.has_value()) { |
| button->SetID(*view_id_); |
| } |
| if (enabled_.has_value()) { |
| button->SetEnabled(*enabled_); |
| } |
| if (visible_.has_value()) { |
| button->SetVisible(*visible_); |
| } |
| if (background_image_.has_value()) { |
| button->SetBackgroundImage(*background_image_); |
| } |
| if (background_color_.has_value()) { |
| button->SetBackgroundColor(*background_color_); |
| } |
| if (character_.has_value()) { |
| button->SetSymbol(*character_); |
| } |
| |
| return button; |
| } |
| |
| IconButton::Builder& IconButton::Builder::SetCallback( |
| PressedCallback callback) { |
| callback_ = std::move(callback); |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetType(Type type) { |
| type_ = type; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetVectorIcon( |
| const gfx::VectorIcon* icon) { |
| CHECK(icon); |
| icon_ = icon; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetSymbol( |
| base_icu::UChar32 character) { |
| character_ = character; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetAccessibleNameId( |
| int accessible_name_id) { |
| accessible_name_ = accessible_name_id; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetAccessibleName( |
| const std::u16string& accessible_name) { |
| accessible_name_ = accessible_name; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetTogglable(bool is_togglable) { |
| is_togglable_ = is_togglable; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetBorder(bool has_border) { |
| has_border_ = has_border; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetViewId(int view_id) { |
| view_id_ = view_id; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetEnabled(bool enabled) { |
| enabled_ = enabled; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetVisible(bool visible) { |
| visible_ = visible; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetBackgroundImage( |
| const gfx::ImageSkia& background_image) { |
| background_image_ = background_image; |
| return *this; |
| } |
| IconButton::Builder& IconButton::Builder::SetBackgroundColor( |
| ui::ColorId background_color) { |
| background_color_ = background_color; |
| return *this; |
| } |
| |
| IconButton::IconButton(PressedCallback callback, |
| IconButton::Type type, |
| const gfx::VectorIcon* icon, |
| int accessible_name_id) |
| : IconButton(std::move(callback), |
| type, |
| icon, |
| accessible_name_id, |
| /*is_togglable=*/false, |
| /*has_border=*/false) {} |
| |
| IconButton::IconButton(PressedCallback callback, |
| IconButton::Type type, |
| const gfx::VectorIcon* icon, |
| bool is_togglable, |
| bool has_border) |
| : views::ImageButton(std::move(callback)), |
| type_(type), |
| icon_(icon), |
| is_togglable_(is_togglable), |
| background_toggled_color_(kDefaultToggledBackgroundColorId), |
| icon_color_(GetDefaultIconColorId(type, /*focused=*/false)), |
| icon_toggled_color_(kDefaultToggledIconColorId) { |
| const int button_size = GetButtonSizeOnType(type); |
| SetPreferredSize(gfx::Size(button_size, button_size)); |
| |
| SetImageHorizontalAlignment(ALIGN_CENTER); |
| SetImageVerticalAlignment(ALIGN_MIDDLE); |
| StyleUtil::SetUpInkDropForButton(this, gfx::Insets(), |
| /*highlight_on_hover=*/false, |
| /*highlight_on_focus=*/false); |
| |
| if (!IsFloatingIconButton(type)) { |
| background_color_ = GetDefaultBackgroundColorId(type).value(); |
| } |
| |
| UpdateBackground(); |
| if (icon_) { |
| UpdateVectorIcon(); |
| } |
| UpdateAccessibilityProperties(); |
| |
| auto* focus_ring = views::FocusRing::Get(this); |
| focus_ring->SetOutsetFocusRingDisabled(true); |
| focus_ring->SetColorId(cros_tokens::kCrosSysFocusRing); |
| if (has_border) { |
| // The focus ring will have the outline padding with the bounds of the |
| // buttons. |
| focus_ring->SetPathGenerator( |
| std::make_unique<views::CircleHighlightPathGenerator>(-gfx::Insets( |
| focus_ring->GetHaloThickness() / 2 + kFocusRingPadding))); |
| } |
| |
| views::InstallCircleHighlightPathGenerator(this); |
| |
| enabled_changed_subscription_ = AddEnabledChangedCallback(base::BindRepeating( |
| &IconButton::OnEnabledStateChanged, base::Unretained(this))); |
| } |
| |
| IconButton::IconButton(PressedCallback callback, |
| IconButton::Type type, |
| const gfx::VectorIcon* icon, |
| const std::u16string& accessible_name, |
| bool is_togglable, |
| bool has_border) |
| : IconButton(std::move(callback), type, icon, is_togglable, has_border) { |
| SetTooltipText(accessible_name); |
| } |
| |
| IconButton::IconButton(PressedCallback callback, |
| IconButton::Type type, |
| const gfx::VectorIcon* icon, |
| int accessible_name_id, |
| bool is_togglable, |
| bool has_border) |
| : IconButton(std::move(callback), |
| type, |
| icon, |
| l10n_util::GetStringUTF16(accessible_name_id), |
| is_togglable, |
| has_border) {} |
| |
| IconButton::~IconButton() = default; |
| |
| void IconButton::SetButtonBehavior(DisabledButtonBehavior button_behavior) { |
| if (button_behavior_ == button_behavior) { |
| return; |
| } |
| |
| button_behavior_ = button_behavior; |
| // Change button behavior may impact the toggled state. |
| if (toggled_ && !GetEnabled()) { |
| UpdateVectorIcon(); |
| } |
| } |
| |
| void IconButton::SetVectorIcon(const gfx::VectorIcon& icon) { |
| icon_ = &icon; |
| if (!IsToggledOn()) { |
| UpdateVectorIcon(); |
| } |
| } |
| |
| void IconButton::SetSymbol(base_icu::UChar32 character) { |
| CHECK(base::IsValidCodepoint(character)); |
| character_ = character; |
| UpdateVectorIcon(); |
| } |
| |
| void IconButton::SetToggledVectorIcon(const gfx::VectorIcon& icon) { |
| toggled_icon_ = &icon; |
| if (IsToggledOn()) { |
| UpdateVectorIcon(); |
| } |
| } |
| |
| void IconButton::SetBackgroundColor(ui::ColorVariant background_color) { |
| if (background_color_ == background_color) { |
| return; |
| } |
| |
| background_color_ = background_color; |
| if (GetEnabled() && !IsToggledOn()) { |
| UpdateBackground(); |
| } |
| } |
| |
| void IconButton::SetBackgroundToggledColor( |
| ui::ColorVariant background_toggled_color) { |
| if (!is_togglable_ || background_toggled_color == background_toggled_color_) { |
| return; |
| } |
| |
| background_toggled_color_ = background_toggled_color; |
| if (GetEnabled() && IsToggledOn()) { |
| UpdateBackground(); |
| } |
| } |
| |
| void IconButton::SetBackgroundImage(const gfx::ImageSkia& background_image) { |
| background_image_ = gfx::ImageSkiaOperations::CreateResizedImage( |
| background_image, skia::ImageOperations::RESIZE_BEST, GetPreferredSize()); |
| SchedulePaint(); |
| } |
| |
| void IconButton::SetIconColor(ui::ColorVariant icon_color) { |
| if (icon_color_ == icon_color) { |
| return; |
| } |
| |
| icon_color_ = icon_color; |
| if (!IsToggledOn()) { |
| UpdateVectorIcon(/*color_changes_only=*/true); |
| } |
| } |
| |
| void IconButton::SetIconToggledColor(ui::ColorVariant icon_toggled_color) { |
| if (!is_togglable_ || icon_toggled_color == icon_toggled_color_) { |
| return; |
| } |
| |
| icon_toggled_color_ = icon_toggled_color; |
| if (IsToggledOn()) { |
| UpdateVectorIcon(/*color_changes_only=*/true); |
| } |
| } |
| |
| void IconButton::SetIconSize(int size) { |
| if (icon_size_ == size) { |
| return; |
| } |
| |
| icon_size_ = size; |
| UpdateVectorIcon(); |
| } |
| |
| void IconButton::SetToggled(bool toggled) { |
| if (!is_togglable_ || toggled_ == toggled) { |
| return; |
| } |
| |
| toggled_ = toggled; |
| |
| UpdateAccessibilityProperties(); |
| |
| if (GetEnabled()) { |
| UpdateBackground(); |
| } |
| |
| // If toggle state is changed with `toggled_`, update the icon. |
| if (GetEnabled() || |
| button_behavior_ == |
| DisabledButtonBehavior::kCanDisplayDisabledToggleValue) { |
| UpdateVectorIcon(); |
| } |
| } |
| |
| void IconButton::SetEnableBlurredBackgroundShield(bool enable) { |
| if (blurred_background_shield_enabled_ == enable) { |
| return; |
| } |
| blurred_background_shield_enabled_ = enable; |
| if (blurred_background_shield_enabled_) { |
| SetBackground(nullptr); |
| } else { |
| blurred_background_shield_.reset(); |
| } |
| UpdateBackground(); |
| } |
| |
| void IconButton::OnFocus() { |
| // Update prominent floating type button's icon color on focus. |
| if (IsProminentFloatingType(type_) && !IsToggledOn()) { |
| // If prominent floating button is still using default colors, updates its |
| // icon color on focus. |
| if (icon_color_.GetColorId() == |
| GetDefaultIconColorId(type_, /*focused=*/false)) { |
| icon_color_ = GetDefaultIconColorId(type_, /*focused=*/true); |
| UpdateVectorIcon(/*color_changes_only=*/true); |
| } |
| } |
| } |
| |
| void IconButton::OnBlur() { |
| // Update prominent floating type button's icon color on blur. |
| if (IsProminentFloatingType(type_) && !IsToggledOn()) { |
| // If prominent floating button is still using default colors, updates its |
| // icon color on focus. |
| if (icon_color_.GetColorId() == |
| GetDefaultIconColorId(type_, /*focused=*/true)) { |
| icon_color_ = GetDefaultIconColorId(type_, /*focused=*/false); |
| UpdateVectorIcon(/*color_changes_only=*/true); |
| } |
| } |
| } |
| |
| void IconButton::PaintButtonContents(gfx::Canvas* canvas) { |
| if (!IsFloatingIconButton(type_) || IsToggledOn()) { |
| // Apply the background image. This is painted on top of the |color|. |
| if (!background_image_.isNull()) { |
| const gfx::Rect rect(GetContentsBounds()); |
| cc::PaintFlags flags; |
| flags.setAntiAlias(true); |
| SkPath mask; |
| mask.addCircle(rect.CenterPoint().x(), rect.CenterPoint().y(), |
| rect.width() / 2); |
| canvas->ClipPath(mask, true); |
| canvas->DrawImageInt(background_image_, 0, 0, flags); |
| } |
| } |
| |
| views::ImageButton::PaintButtonContents(canvas); |
| } |
| |
| void IconButton::NotifyClick(const ui::Event& event) { |
| if (is_togglable_) { |
| chromeos::haptics_util::PlayHapticToggleEffect( |
| !toggled_, ui::HapticTouchpadEffectStrength::kMedium); |
| } |
| |
| views::Button::NotifyClick(event); |
| } |
| |
| void IconButton::UpdateBackground() { |
| if (blurred_background_shield_enabled_) { |
| UpdateBlurredBackgroundShield(); |
| return; |
| } |
| |
| // The untoggled floating button does not have a background. |
| const bool is_toggled = IsToggledOn(); |
| if (IsFloatingIconButton(type_) && !is_toggled) { |
| SetBackground(nullptr); |
| return; |
| } |
| |
| // Create a themed rounded rect background when the button is disabled. |
| if (!GetEnabled()) { |
| SetBackground( |
| CreateThemedBackground(cros_tokens::kCrosSysDisabledContainer, type_)); |
| return; |
| } |
| |
| // Create a background according to the toggled state. |
| ui::ColorVariant color_variant = |
| is_toggled ? background_toggled_color_ : background_color_; |
| SetBackground(CreateSolidBackground(color_variant, type_)); |
| } |
| |
| void IconButton::UpdateBlurredBackgroundShield() { |
| CHECK(blurred_background_shield_enabled_); |
| const bool is_toggled = IsToggledOn(); |
| if (IsFloatingIconButton(type_) && !is_toggled) { |
| blurred_background_shield_.reset(); |
| return; |
| } |
| |
| // Create a new blurred background shield if needed. |
| if (!blurred_background_shield_) { |
| blurred_background_shield_ = std::make_unique<BlurredBackgroundShield>( |
| this, background_color_, ColorProvider::kBackgroundBlurSigma, |
| gfx::RoundedCornersF(GetButtonSizeOnType(type_) / 2)); |
| } |
| |
| ui::ColorVariant color_variant = |
| GetEnabled() |
| ? (is_toggled ? background_toggled_color_ : background_color_) |
| : cros_tokens::kCrosSysDisabledContainer; |
| blurred_background_shield_->SetColor(color_variant); |
| } |
| |
| void IconButton::UpdateVectorIcon(bool color_changes_only) { |
| std::pair<ui::ImageModel, ui::ImageModel> images; |
| |
| const int icon_size = icon_size_.value_or(GetIconSizeOnType(type_)); |
| const bool is_toggled = IsToggledOn(); |
| ui::ColorVariant color_variant = |
| is_toggled ? icon_toggled_color_ : icon_color_; |
| |
| if (character_.has_value()) { |
| images = SymbolImages(is_toggled, *color_variant.GetColorId(), icon_size, |
| *character_); |
| } else { |
| images = VectorImages(is_toggled, color_variant, icon_size); |
| } |
| |
| if (images.first.IsEmpty()) { |
| return; |
| } |
| |
| ui::ImageModel new_normal_image_model = images.first; |
| |
| if (GetWidget()) { |
| // Skip repainting if the incoming icon is the same as the current icon. If |
| // the icon has been painted before, |gfx::CreateVectorIcon()| will simply |
| // grab the ImageSkia from a cache, so it will be cheap. Note that this |
| // assumes that toggled/disabled images changes at the same time as the |
| // normal image, which it currently does. |
| const gfx::ImageSkia new_normal_image = |
| new_normal_image_model.Rasterize(GetColorProvider()); |
| const gfx::ImageSkia& old_normal_image = |
| GetImage(views::Button::STATE_NORMAL); |
| if (!new_normal_image.isNull() && !old_normal_image.isNull() && |
| new_normal_image.BackedBySameObjectAs(old_normal_image)) { |
| return; |
| } |
| } |
| |
| SetImageModel(views::Button::STATE_NORMAL, new_normal_image_model); |
| if (!color_changes_only) { |
| SetImageModel(views::Button::STATE_DISABLED, images.second); |
| } |
| } |
| |
| void IconButton::OnEnabledStateChanged() { |
| // Enabled state change may cause toggled state change. |
| if (toggled_ && button_behavior_ != |
| DisabledButtonBehavior::kCanDisplayDisabledToggleValue) { |
| UpdateVectorIcon(); |
| } |
| UpdateBackground(); |
| } |
| |
| bool IconButton::IsToggledOn() const { |
| return toggled_ && |
| (GetEnabled() || |
| button_behavior_ == |
| DisabledButtonBehavior::kCanDisplayDisabledToggleValue); |
| } |
| |
| void IconButton::UpdateAccessibilityProperties() { |
| if (is_togglable_) { |
| GetViewAccessibility().SetRole(ax::mojom::Role::kToggleButton); |
| GetViewAccessibility().SetCheckedState( |
| toggled_ ? ax::mojom::CheckedState::kTrue |
| : ax::mojom::CheckedState::kFalse); |
| } else { |
| GetViewAccessibility().SetRole(ax::mojom::Role::kButton); |
| GetViewAccessibility().RemoveCheckedState(); |
| } |
| } |
| |
| std::pair<ui::ImageModel, ui::ImageModel> IconButton::VectorImages( |
| const bool is_toggled, |
| const ui::ColorVariant color, |
| const int icon_size) { |
| const gfx::VectorIcon* icon = |
| is_toggled && toggled_icon_ ? toggled_icon_.get() : icon_.get(); |
| |
| if (!icon) { |
| return {ui::ImageModel(), ui::ImageModel()}; |
| } |
| |
| ui::ImageModel new_normal_image_model = |
| ui::ImageModel::FromVectorIcon(*icon, color, icon_size); |
| |
| ui::ImageModel disabled_image_model = ui::ImageModel::FromVectorIcon( |
| *icon, cros_tokens::kCrosSysDisabled, icon_size); |
| |
| return {std::move(new_normal_image_model), std::move(disabled_image_model)}; |
| } |
| |
| BEGIN_METADATA(IconButton) |
| END_METADATA |
| |
| } // namespace ash |