diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 310d0785e020c..283e1e6a8fca4 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn @@ -1086,8 +1086,6 @@ component("ash") { "style/icon_button.h", "style/icon_switch.cc", "style/icon_switch.h", - "style/knob_switch.cc", - "style/knob_switch.h", "style/option_button_base.cc", "style/option_button_base.h", "style/option_button_group.cc", @@ -1113,11 +1111,11 @@ component("ash") { "style/style_viewer/checkbox_instances_grid_view_factory.cc", "style/style_viewer/icon_button_instances_grid_view_factory.cc", "style/style_viewer/icon_switch_instances_grid_view_factory.cc", - "style/style_viewer/knob_switch_instances_grid_view_factory.cc", "style/style_viewer/pagination_instances_grid_view_factory.cc", "style/style_viewer/pill_button_instances_grid_view_factory.cc", "style/style_viewer/radio_button_group_instances_grid_view_factory.cc", "style/style_viewer/radio_button_instances_grid_view_factory.cc", + "style/style_viewer/switch_instances_grid_view_factory.cc", "style/style_viewer/system_text_instances_grid_view_factory.cc", "style/style_viewer/system_ui_components_grid_view.cc", "style/style_viewer/system_ui_components_grid_view.h", @@ -1126,6 +1124,8 @@ component("ash") { "style/style_viewer/system_ui_components_style_viewer_view.h", "style/style_viewer/tab_slider_instances_grid_view_factory.cc", "style/style_viewer/typography_instances_grid_view_factory.cc", + "style/switch.cc", + "style/switch.h", "style/system_shadow.cc", "style/system_shadow.h", "style/system_shadow_on_nine_patch_layer.cc", diff --git a/ash/style/ash_color_mixer.cc b/ash/style/ash_color_mixer.cc index 13038d847180b..2241eb21a48e4 100644 --- a/ash/style/ash_color_mixer.cc +++ b/ash/style/ash_color_mixer.cc @@ -602,6 +602,20 @@ void AddAshColorMixer(ui::ColorProvider* provider, mixer[kColorAshFolderItemCountBackgroundColor] = use_dark_color ? ui::ColorTransform(gfx::kGoogleBlue300) : ui::ColorTransform(gfx::kGoogleBlue600); + + mixer[ui::kColorToggleButtonThumbOn] = {cros_tokens::kCrosSysOnPrimary}; + mixer[ui::kColorToggleButtonThumbOff] = {cros_tokens::kCrosSysOnSecondary}; + mixer[ui::kColorToggleButtonThumbOnDisabled] = + ui::SetAlpha({ui::kColorToggleButtonThumbOn}, kDisabledColorOpacity); + mixer[ui::kColorToggleButtonThumbOffDisabled] = + ui::SetAlpha({ui::kColorToggleButtonThumbOff}, kDisabledColorOpacity); + mixer[ui::kColorToggleButtonTrackOn] = {cros_tokens::kCrosSysPrimary}; + mixer[ui::kColorToggleButtonTrackOff] = {cros_tokens::kCrosSysSecondary}; + mixer[ui::kColorToggleButtonTrackOnDisabled] = + ui::SetAlpha({ui::kColorToggleButtonTrackOn}, kDisabledColorOpacity); + mixer[ui::kColorToggleButtonTrackOffDisabled] = + ui::SetAlpha({ui::kColorToggleButtonTrackOff}, kDisabledColorOpacity); + mixer[ui::kColorToggleButtonHover] = {cros_tokens::kCrosSysHoverOnProminent}; } } // namespace ash diff --git a/ash/style/knob_switch.cc b/ash/style/knob_switch.cc deleted file mode 100644 index f77e967519023..0000000000000 --- a/ash/style/knob_switch.cc +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2022 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/knob_switch.h" - -#include "ash/style/color_util.h" -#include "ash/style/style_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/views/background.h" -#include "ui/views/controls/highlight_path_generator.h" - -namespace ash { - -namespace { - -// Switch, track, and knob size. -constexpr int kSwitchWidth = 48; -constexpr int kSwitchHeight = 32; -constexpr int kSwitchInnerPadding = 8; -constexpr int kTrackInnerPadding = 2; -constexpr int kKnobRadius = 6; -constexpr int kFocusPadding = 2; - -// Track and knob color ids. -constexpr ui::ColorId kSelectedTrackColorId = cros_tokens::kCrosSysPrimary; -constexpr ui::ColorId kSelectedKnobColorId = cros_tokens::kCrosSysOnPrimary; -constexpr ui::ColorId kUnSelectedTrackColorId = cros_tokens::kCrosSysSecondary; -constexpr ui::ColorId kUnSelectedKnobColorId = cros_tokens::kCrosSysOnSecondary; - -} // namespace - -//------------------------------------------------------------------------------ -// KnobSwitch: - -KnobSwitch::KnobSwitch(KnobSwitch::Callback switch_callback) - : switch_callback_(std::move(switch_callback)) { - // Build view hierarchy. The track view and knob view cannot be focused and - // process event. - views::Builder(this) - .SetBorder(views::CreateEmptyBorder(gfx::Insets(kSwitchInnerPadding))) - .SetPreferredSize(gfx::Size(kSwitchWidth, kSwitchHeight)) - .SetUseDefaultFillLayout(true) - .AddChildren( - views::Builder() - .CopyAddressTo(&track_) - .SetFocusBehavior(views::View::FocusBehavior::NEVER) - .SetPaintToLayer() - .SetCanProcessEventsWithinSubtree(false) - .SetBorder( - views::CreateEmptyBorder(gfx::Insets(kTrackInnerPadding))) - .SetBackground(StyleUtil::CreateThemedFullyRoundedRectBackground( - kUnSelectedTrackColorId)) - .AddChildren( - views::Builder() - .CopyAddressTo(&knob_) - .SetFocusBehavior(views::View::FocusBehavior::NEVER) - .SetPaintToLayer() - .SetCanProcessEventsWithinSubtree(false) - .SetPreferredSize( - gfx::Size(2 * kKnobRadius, 2 * kKnobRadius)) - .SetBackground( - StyleUtil::CreateThemedFullyRoundedRectBackground( - kUnSelectedKnobColorId)))) - .BuildChildren(); - - track_->layer()->SetFillsBoundsOpaquely(false); - knob_->layer()->SetFillsBoundsOpaquely(false); - - // Install a pill shaped focus ring on the track. - auto* focus_ring = views::FocusRing::Get(this); - focus_ring->SetColorId(cros_tokens::kCrosSysFocusRing); - const float halo_inset = focus_ring->GetHaloThickness() / 2.f + kFocusPadding; - focus_ring->SetHaloInset(-halo_inset); - auto pill_shape_path = std::make_unique(); - pill_shape_path->set_use_contents_bounds(true); - views::HighlightPathGenerator::Install(this, std::move(pill_shape_path)); - - // The switch is unselected initially. - if (switch_callback_) - switch_callback_.Run(false); -} - -KnobSwitch::~KnobSwitch() = default; - -void KnobSwitch::SetSelected(bool selected) { - if (selected_ == selected) - return; - - selected_ = selected; - - // Update the track and knob colors. - const ui::ColorId knob_color_id = - selected_ ? kSelectedKnobColorId : kUnSelectedKnobColorId; - const ui::ColorId track_color_id = - selected_ ? kSelectedTrackColorId : kUnSelectedTrackColorId; - knob_->SetBackground( - StyleUtil::CreateThemedFullyRoundedRectBackground(knob_color_id)); - track_->SetBackground( - StyleUtil::CreateThemedFullyRoundedRectBackground(track_color_id)); - - Layout(); - SchedulePaint(); - - if (switch_callback_) - switch_callback_.Run(selected_); -} - -void KnobSwitch::Layout() { - views::Button::Layout(); - - // If selected, move the knob to the right. Otherwise, move knob to the left. - const gfx::Rect track_contents_bounds = track_->GetContentsBounds(); - const int knob_x = selected_ ? track_contents_bounds.right() - 2 * kKnobRadius - : track_contents_bounds.x(); - const int knob_y = track_contents_bounds.y(); - knob_->SizeToPreferredSize(); - knob_->SetPosition(gfx::Point(knob_x, knob_y)); -} - -int KnobSwitch::GetHeightForWidth(int w) const { - return kSwitchHeight; -} - -void KnobSwitch::StateChanged(ButtonState old_state) { - if (GetState() == ButtonState::STATE_DISABLED) { - track_->SetEnabled(false); - knob_->SetEnabled(false); - } else if (old_state == ButtonState::STATE_DISABLED) { - track_->SetEnabled(true); - knob_->SetEnabled(true); - } -} - -void KnobSwitch::NotifyClick(const ui::Event& event) { - // Switch the current selected state on click. - SetSelected(!selected_); -} - -BEGIN_METADATA(KnobSwitch, views::View) -END_METADATA - -} // namespace ash diff --git a/ash/style/knob_switch.h b/ash/style/knob_switch.h deleted file mode 100644 index 5a6c10294fcbd..0000000000000 --- a/ash/style/knob_switch.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_STYLE_KNOB_SWITCH_H_ -#define ASH_STYLE_KNOB_SWITCH_H_ - -#include "ash/ash_export.h" -#include "ui/base/metadata/metadata_header_macros.h" -#include "ui/views/controls/button/button.h" - -namespace ash { - -class ASH_EXPORT KnobSwitch : public views::Button { - public: - METADATA_HEADER(KnobSwitch); - - using Callback = base::RepeatingCallback; - - explicit KnobSwitch(Callback switch_callback = Callback()); - KnobSwitch(const KnobSwitch&) = delete; - KnobSwitch& operator=(const KnobSwitch&) = delete; - ~KnobSwitch() override; - - void SetSelected(bool selected); - bool selected() const { return selected_; } - - // views::View: - void Layout() override; - int GetHeightForWidth(int w) const override; - - private: - // views::Button: - void StateChanged(ButtonState old_state) override; - void NotifyClick(const ui::Event& event) override; - - Callback switch_callback_; - - // Owned by switch view. - views::View* track_ = nullptr; - // Owned by tracker view. - views::View* knob_ = nullptr; - - bool selected_ = false; -}; - -BEGIN_VIEW_BUILDER(ASH_EXPORT, KnobSwitch, views::Button) -END_VIEW_BUILDER - -} // namespace ash - -DEFINE_VIEW_BUILDER(ASH_EXPORT, ash::KnobSwitch) - -#endif // ASH_STYLE_KNOB_SWITCH_H_ diff --git a/ash/style/style_viewer/knob_switch_instances_grid_view_factory.cc b/ash/style/style_viewer/switch_instances_grid_view_factory.cc similarity index 68% rename from ash/style/style_viewer/knob_switch_instances_grid_view_factory.cc rename to ash/style/style_viewer/switch_instances_grid_view_factory.cc index 88314dd4e3ec4..d88b3f4e9181e 100644 --- a/ash/style/style_viewer/knob_switch_instances_grid_view_factory.cc +++ b/ash/style/style_viewer/switch_instances_grid_view_factory.cc @@ -1,11 +1,11 @@ -// Copyright 2022 The Chromium Authors +// Copyright 2023 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/style_viewer/system_ui_components_grid_view_factories.h" -#include "ash/style/knob_switch.h" #include "ash/style/style_viewer/system_ui_components_grid_view.h" +#include "ash/style/switch.h" #include "ui/views/controls/label.h" namespace ash { @@ -19,8 +19,8 @@ constexpr size_t kGridViewRowGroupSize = 2; constexpr size_t kGirdViewColGroupSize = 1; // A callback function of knob switch to show its selected state on a label. -void ShowSwitchState(views::Label* label, bool selected) { - if (selected) { +void ShowSwitchState(views::Label* label, Switch* switch_view) { + if (switch_view->GetIsOn()) { label->SetText(u"Switch is ON"); return; } @@ -30,32 +30,32 @@ void ShowSwitchState(views::Label* label, bool selected) { } // namespace -std::unique_ptr -CreateKnobSwitchInstancesGridView() { +std::unique_ptr CreateSwitchInstancesGridView() { auto grid_view = std::make_unique( kGridViewRowNum, kGridViewColNum, kGridViewRowGroupSize, kGirdViewColGroupSize); // A label used to show the selected state of a knob switch. - auto label = std::make_unique(); + auto label = std::make_unique(u"Switch is OFF"); label->SetAccessibleName(u"switch state"); - auto knob_switch = std::make_unique( - base::BindRepeating(&ShowSwitchState, label.get())); - knob_switch->SetAccessibleName(u"knob switch"); + auto switch_view = std::make_unique(); + switch_view->SetCallback( + base::BindRepeating(&ShowSwitchState, label.get(), switch_view.get())); + switch_view->SetAccessibleName(u"switch"); // A disabled knob switch with selected off state. - auto disabled_switch_off = std::make_unique(); + auto disabled_switch_off = std::make_unique(); disabled_switch_off->SetEnabled(false); disabled_switch_off->SetAccessibleName(u"disabled switch off"); // A disabled knob switch with selected on state. - auto disabled_switch_on = std::make_unique(); - disabled_switch_on->SetSelected(true); + auto disabled_switch_on = std::make_unique(); + disabled_switch_on->SetIsOn(true); disabled_switch_on->SetEnabled(false); disabled_switch_on->SetAccessibleName(u"disabled switch on"); grid_view->AddInstance(u"", std::move(label)); - grid_view->AddInstance(u"Knob Switch", std::move(knob_switch)); + grid_view->AddInstance(u"Switch", std::move(switch_view)); grid_view->AddInstance(u"Disabled Switch OFF", std::move(disabled_switch_off)); grid_view->AddInstance(u"Disabled Switch ON", std::move(disabled_switch_on)); diff --git a/ash/style/style_viewer/system_ui_components_grid_view_factories.h b/ash/style/style_viewer/system_ui_components_grid_view_factories.h index c205d8a6c50e6..0c08a3f5e8287 100644 --- a/ash/style/style_viewer/system_ui_components_grid_view_factories.h +++ b/ash/style/style_viewer/system_ui_components_grid_view_factories.h @@ -23,7 +23,7 @@ std::unique_ptr CreateRadioButtonInstancesGridView(); std::unique_ptr CreateRadioButtonGroupInstancesGridView(); -std::unique_ptr CreateKnobSwitchInstancesGridView(); +std::unique_ptr CreateSwitchInstancesGridView(); std::unique_ptr CreateTabSliderInstancesGridView(); std::unique_ptr CreateSystemTextfieldInstancesGridView(); diff --git a/ash/style/style_viewer/system_ui_components_style_viewer_view.cc b/ash/style/style_viewer/system_ui_components_style_viewer_view.cc index 8745a9651bedb..4cb4e4cf66535 100644 --- a/ash/style/style_viewer/system_ui_components_style_viewer_view.cc +++ b/ash/style/style_viewer/system_ui_components_style_viewer_view.cc @@ -167,7 +167,7 @@ void SystemUIComponentsStyleViewerView::CreateAndShowWidget() { u"RadioButtonGroup", base::BindRepeating(&CreateRadioButtonGroupInstancesGridView)); viewer_view->AddComponent( - u"KnobSwitch", base::BindRepeating(&CreateKnobSwitchInstancesGridView)); + u"Switch", base::BindRepeating(&CreateSwitchInstancesGridView)); viewer_view->AddComponent( u"TabSlider", base::BindRepeating(&CreateTabSliderInstancesGridView)); viewer_view->AddComponent( diff --git a/ash/style/switch.cc b/ash/style/switch.cc new file mode 100644 index 0000000000000..8fb2708d395d9 --- /dev/null +++ b/ash/style/switch.cc @@ -0,0 +1,72 @@ +// Copyright 2023 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/switch.h" + +#include "ash/style/switch.h" +#include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/skia_conversions.h" +#include "ui/views/border.h" +#include "ui/views/controls/focus_ring.h" + +namespace ash { + +namespace { + +// Switch, track, and knob size. +constexpr int kSwitchWidth = 48; +constexpr int kSwitchHeight = 32; +constexpr int kSwitchInnerPadding = 8; +constexpr int kTrackInnerPadding = 2; +constexpr int kThumbRadius = 6; +constexpr int kFocusPadding = 2; + +} // namespace + +//------------------------------------------------------------------------------ +// Switch: + +Switch::Switch(PressedCallback callback) + : views::ToggleButton(callback, /*has_thumb_shadow=*/false) { + SetBorder(views::CreateEmptyBorder(gfx::Insets(kSwitchInnerPadding))); +} + +Switch::~Switch() = default; + +gfx::Size Switch::CalculatePreferredSize() const { + return gfx::Size(kSwitchWidth, kSwitchHeight); +} + +SkPath Switch::GetFocusRingPath() const { + gfx::Rect bounds = GetTrackBounds(); + const auto* focus_ring = views::FocusRing::Get(this); + const int focus_ring_thickness = + focus_ring ? focus_ring->GetHaloThickness() + : views::FocusRing::kDefaultHaloThickness; + bounds.Inset(-gfx::Insets(kFocusPadding + focus_ring_thickness / 2)); + + const SkScalar radius = SkIntToScalar(bounds.height() / 2); + return SkPath::RRect(gfx::RectToSkRect(bounds), radius, radius); +} + +gfx::Rect Switch::GetTrackBounds() const { + return GetContentsBounds(); +} + +gfx::Rect Switch::GetThumbBounds() const { + gfx::Rect bounds = GetTrackBounds(); + bounds.Inset(gfx::Insets(kTrackInnerPadding)); + + const int thumb_size = 2 * kThumbRadius; + bounds.set_x(bounds.x() + + GetAnimationProgress() * (bounds.width() - thumb_size)); + bounds.set_size(gfx::Size(thumb_size, thumb_size)); + return bounds; +} + +BEGIN_METADATA(Switch, views::ToggleButton) +END_METADATA + +} // namespace ash diff --git a/ash/style/switch.h b/ash/style/switch.h new file mode 100644 index 0000000000000..c5a87f76bc2ba --- /dev/null +++ b/ash/style/switch.h @@ -0,0 +1,35 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_STYLE_SWITCH_H_ +#define ASH_STYLE_SWITCH_H_ + +#include "ash/ash_export.h" +#include "ui/base/metadata/metadata_header_macros.h" +#include "ui/views/controls/button/toggle_button.h" + +namespace ash { + +class ASH_EXPORT Switch : public views::ToggleButton { + public: + METADATA_HEADER(Switch); + + explicit Switch(PressedCallback callback = PressedCallback()); + Switch(const Switch&) = delete; + Switch& operator=(const Switch&) = delete; + ~Switch() override; + + // views::View: + gfx::Size CalculatePreferredSize() const override; + + private: + // views::ToggleButton: + SkPath GetFocusRingPath() const override; + gfx::Rect GetTrackBounds() const override; + gfx::Rect GetThumbBounds() const override; +}; + +} // namespace ash + +#endif // ASH_STYLE_SWITCH_H_ diff --git a/ui/color/color_id.h b/ui/color/color_id.h index 551d05841b2d1..e25a87d2710ed 100644 --- a/ui/color/color_id.h +++ b/ui/color/color_id.h @@ -399,6 +399,7 @@ E_CPONLY(kColorToggleButtonThumbOnDisabled) \ E_CPONLY(kColorToggleButtonThumbOnHover) \ E_CPONLY(kColorToggleButtonTrackOff) \ + E_CPONLY(kColorToggleButtonTrackOffDisabled) \ E_CPONLY(kColorToggleButtonTrackOn) \ E_CPONLY(kColorToggleButtonTrackOnDisabled) \ E_CPONLY(kColorToolbarSearchFieldBackground) \ diff --git a/ui/color/material_ui_color_mixer.cc b/ui/color/material_ui_color_mixer.cc index 484dd8ac5a29b..771069f2e8643 100644 --- a/ui/color/material_ui_color_mixer.cc +++ b/ui/color/material_ui_color_mixer.cc @@ -84,6 +84,7 @@ void AddMaterialUiColorMixer(ColorProvider* provider, mixer[kColorToggleButtonThumbOnDisabled] = {kColorSysSurface}; mixer[kColorToggleButtonThumbOnHover] = {kColorSysPrimaryContainer}; mixer[kColorToggleButtonTrackOff] = {kColorSysSurfaceVariant}; + mixer[kColorToggleButtonTrackOffDisabled] = {kColorSysSurfaceVariant}; mixer[kColorToggleButtonTrackOn] = {kColorSysPrimary}; mixer[kColorToggleButtonTrackOnDisabled] = {kColorSysStateDisabledContainer}; mixer[kColorToolbarSearchFieldBackground] = {kColorSysBaseContainerElevated}; diff --git a/ui/views/controls/button/toggle_button.cc b/ui/views/controls/button/toggle_button.cc index ad547c3266df4..d1056314480be 100644 --- a/ui/views/controls/button/toggle_button.cc +++ b/ui/views/controls/button/toggle_button.cc @@ -8,6 +8,7 @@ #include #include +#include "base/callback_list.h" #include "base/functional/bind.h" #include "cc/paint/paint_flags.h" #include "third_party/skia/include/core/SkDrawLooper.h" @@ -66,6 +67,21 @@ int GetThumbInset(bool is_on) { return kThumbInset; } +absl::optional GetSkColorFromVariant( + const absl::variant& color_variant) { + return absl::holds_alternative(color_variant) + ? absl::make_optional(absl::get(color_variant)) + : absl::nullopt; +} + +SkColor ConvertVariantToSkColor( + const absl::variant color_variant, + const ui::ColorProvider* color_provider) { + return absl::holds_alternative(color_variant) + ? absl::get(color_variant) + : color_provider->GetColor(absl::get(color_variant)); +} + } // namespace class ToggleButton::FocusRingHighlightPathGenerator @@ -80,7 +96,7 @@ class ToggleButton::FocusRingHighlightPathGenerator class ToggleButton::ThumbView : public View { public: METADATA_HEADER(ThumbView); - ThumbView() { + explicit ThumbView(bool has_shadow) : has_shadow_(has_shadow) { // Make the thumb behave as part of the parent for event handling. SetCanProcessEventsWithinSubtree(false); } @@ -103,19 +119,18 @@ class ToggleButton::ThumbView : public View { // Returns the extra space needed to draw the shadows around the thumb. Since // the extra space is around the thumb, the insets will be negative. - static gfx::Insets GetShadowOutsets() { - return features::IsChromeRefresh2023() - ? gfx::Insets() - : gfx::Insets(-kShadowBlur) + - gfx::Vector2d(kShadowOffsetX, kShadowOffsetY); + gfx::Insets GetShadowOutsets() { + return has_shadow_ ? gfx::Insets(-kShadowBlur) + + gfx::Vector2d(kShadowOffsetX, kShadowOffsetY) + : gfx::Insets(); } - void SetThumbColor(bool is_on, const absl::optional& thumb_color) { + void SetThumbColor(bool is_on, SkColor thumb_color) { (is_on ? thumb_on_color_ : thumb_off_color_) = thumb_color; } absl::optional GetThumbColor(bool is_on) const { - return is_on ? thumb_on_color_ : thumb_off_color_; + return GetSkColorFromVariant(is_on ? thumb_on_color_ : thumb_off_color_); } private: @@ -128,7 +143,7 @@ class ToggleButton::ThumbView : public View { const float dsf = canvas->UndoDeviceScaleFactor(); const ui::ColorProvider* color_provider = GetColorProvider(); cc::PaintFlags thumb_flags; - if (!features::IsChromeRefresh2023()) { + if (has_shadow_) { std::vector shadows; gfx::ShadowValue shadow( gfx::Vector2d(kShadowOffsetX, kShadowOffsetY), 2 * kShadowBlur, @@ -137,10 +152,10 @@ class ToggleButton::ThumbView : public View { thumb_flags.setLooper(gfx::CreateShadowDrawLooper(shadows)); } thumb_flags.setAntiAlias(true); - const SkColor thumb_on_color = thumb_on_color_.value_or( - color_provider->GetColor(ui::kColorToggleButtonThumbOn)); - const SkColor thumb_off_color = thumb_off_color_.value_or( - color_provider->GetColor(ui::kColorToggleButtonThumbOff)); + const SkColor thumb_on_color = + ConvertVariantToSkColor(thumb_on_color_, color_provider); + const SkColor thumb_off_color = + ConvertVariantToSkColor(thumb_off_color_, color_provider); SkColor thumb_color = color_utils::AlphaBlend(thumb_on_color, thumb_off_color, color_ratio_); if (features::IsChromeRefresh2023() && is_hovered_ && is_on_) { @@ -165,9 +180,27 @@ class ToggleButton::ThumbView : public View { thumb_flags); } + void OnEnabledStateChanged() { + // If using default color ID, update it according to the enabled state. + if (absl::holds_alternative(thumb_on_color_)) { + thumb_on_color_ = GetEnabled() ? ui::kColorToggleButtonThumbOn + : ui::kColorToggleButtonThumbOnDisabled; + } + + if (absl::holds_alternative(thumb_off_color_)) { + thumb_off_color_ = GetEnabled() ? ui::kColorToggleButtonThumbOff + : ui::kColorToggleButtonThumbOffDisabled; + } + } + + // Indicate if the thumb has shadow. + const bool has_shadow_; + // Colors used for the thumb. - absl::optional thumb_on_color_; - absl::optional thumb_off_color_; + absl::variant thumb_on_color_ = + ui::kColorToggleButtonThumbOn; + absl::variant thumb_off_color_ = + ui::kColorToggleButtonThumbOff; // Thumb paints differently when on under ChromeRefresh2023. bool is_on_ = false; @@ -177,15 +210,25 @@ class ToggleButton::ThumbView : public View { float color_ratio_ = 0.0f; // Color ratio between 0 and 1 that controls the thumb hover color. float hover_ratio_ = 0.0f; + + // Callback when the enabled state changes. + base::CallbackListSubscription enabled_state_changed_subscription_{ + AddEnabledChangedCallback( + base::BindRepeating(&ThumbView::OnEnabledStateChanged, + base::Unretained(this)))}; }; ToggleButton::ToggleButton(PressedCallback callback) + : ToggleButton(callback, + /*has_thumb_shadow=*/!features::IsChromeRefresh2023()) {} + +ToggleButton::ToggleButton(PressedCallback callback, bool has_thumb_shadow) : Button(std::move(callback)) { slide_animation_.SetSlideDuration(base::Milliseconds(80)); slide_animation_.SetTweenType(gfx::Tween::LINEAR); hover_animation_.SetSlideDuration(base::Milliseconds(250)); hover_animation_.SetTweenType(gfx::Tween::LINEAR); - thumb_view_ = AddChildView(std::make_unique()); + thumb_view_ = AddChildView(std::make_unique(has_thumb_shadow)); InkDrop::Get(this)->SetMode(InkDropHost::InkDropMode::ON); InkDrop::Get(this)->SetLayerRegion(LayerRegion::kAbove); // Do not set a clip, allow the ink drop to burst out. @@ -199,9 +242,10 @@ ToggleButton::ToggleButton(PressedCallback callback) /*highlight_on_focus=*/false, /*show_highlight_on_ripple=*/features::IsChromeRefresh2023()); InkDrop::Get(this)->SetCreateRippleCallback(base::BindRepeating( - [](ToggleButton* host) -> std::unique_ptr { + [](ToggleButton* host, + gfx::Insets insets) -> std::unique_ptr { gfx::Rect rect = host->thumb_view_->GetLocalBounds(); - rect.Inset(-ThumbView::GetShadowOutsets()); + rect.Inset(insets); if (!features::IsChromeRefresh2023()) { return InkDrop::Get(host)->CreateSquareRipple(rect.CenterPoint()); } @@ -218,7 +262,7 @@ ToggleButton::ToggleButton(PressedCallback callback) views::SquareInkDropRipple::ActivatedShape::kCircle); return ripple; }, - this)); + this, -thumb_view_->GetShadowOutsets())); InkDrop::Get(this)->SetBaseColorCallback(base::BindRepeating( [](ToggleButton* host) { return host->GetTrackColor(host->GetIsOn() || host->HasFocus()); @@ -281,8 +325,7 @@ bool ToggleButton::GetIsOn() const { return slide_animation_.IsShowing(); } -void ToggleButton::SetThumbOnColor( - const absl::optional& thumb_on_color) { +void ToggleButton::SetThumbOnColor(SkColor thumb_on_color) { thumb_view_->SetThumbColor(true /* is_on */, thumb_on_color); } @@ -290,8 +333,7 @@ absl::optional ToggleButton::GetThumbOnColor() const { return thumb_view_->GetThumbColor(true); } -void ToggleButton::SetThumbOffColor( - const absl::optional& thumb_off_color) { +void ToggleButton::SetThumbOffColor(SkColor thumb_off_color) { thumb_view_->SetThumbColor(false /* is_on */, thumb_off_color); } @@ -299,22 +341,20 @@ absl::optional ToggleButton::GetThumbOffColor() const { return thumb_view_->GetThumbColor(false); } -void ToggleButton::SetTrackOnColor( - const absl::optional& track_on_color) { +void ToggleButton::SetTrackOnColor(SkColor track_on_color) { track_on_color_ = track_on_color; } absl::optional ToggleButton::GetTrackOnColor() const { - return track_on_color_; + return GetSkColorFromVariant(track_on_color_); } -void ToggleButton::SetTrackOffColor( - const absl::optional& track_off_color) { +void ToggleButton::SetTrackOffColor(SkColor track_off_color) { track_off_color_ = track_off_color; } absl::optional ToggleButton::GetTrackOffColor() const { - return track_off_color_; + return GetSkColorFromVariant(track_off_color_); } void ToggleButton::SetAcceptsEvents(bool accepts_events) { @@ -360,13 +400,17 @@ gfx::Rect ToggleButton::GetThumbBounds() const { (thumb_bounds.width() - thumb_bounds.height())); // The thumb is a circle, so the width should match the height. thumb_bounds.set_width(thumb_bounds.height()); - thumb_bounds.Inset(ThumbView::GetShadowOutsets()); + thumb_bounds.Inset(thumb_view_->GetShadowOutsets()); if (GetState() == STATE_PRESSED && features::IsChromeRefresh2023()) { thumb_bounds.Outset(kRefreshThumbPressedOutset); } return thumb_bounds; } +double ToggleButton::GetAnimationProgress() const { + return slide_animation_.GetCurrentValue(); +} + void ToggleButton::UpdateThumb() { thumb_view_->Update(GetThumbBounds(), static_cast(slide_animation_.GetCurrentValue()), @@ -385,9 +429,8 @@ void ToggleButton::UpdateThumb() { } SkColor ToggleButton::GetTrackColor(bool is_on) const { - absl::optional color = is_on ? track_on_color_ : track_off_color_; - return color.value_or(GetColorProvider()->GetColor( - is_on ? ui::kColorToggleButtonTrackOn : ui::kColorToggleButtonTrackOff)); + return ConvertVariantToSkColor(is_on ? track_on_color_ : track_off_color_, + GetColorProvider()); } SkColor ToggleButton::GetHoverColor() const { @@ -430,6 +473,22 @@ void ToggleButton::NotifyClick(const ui::Event& event) { void ToggleButton::StateChanged(ButtonState old_state) { Button::StateChanged(old_state); + + // Update default track color ID and propagate the enabled state to the thumb. + const bool enabled = GetState() != ButtonState::STATE_DISABLED; + if (absl::holds_alternative(track_on_color_)) { + track_on_color_ = enabled ? ui::kColorToggleButtonTrackOn + : ui::kColorToggleButtonTrackOnDisabled; + } + + if (absl::holds_alternative(track_off_color_)) { + track_off_color_ = enabled ? ui::kColorToggleButtonTrackOff + : ui::kColorToggleButtonTrackOffDisabled; + } + + thumb_view_->SetEnabled(enabled); + + // Update thumb bounds. if (features::IsChromeRefresh2023()) { if (GetState() == STATE_PRESSED || old_state == STATE_PRESSED) { UpdateThumb(); @@ -543,10 +602,6 @@ END_METADATA BEGIN_METADATA(ToggleButton, Button) ADD_PROPERTY_METADATA(bool, IsOn) ADD_PROPERTY_METADATA(bool, AcceptsEvents) -ADD_PROPERTY_METADATA(absl::optional, ThumbOnColor) -ADD_PROPERTY_METADATA(absl::optional, ThumbOffColor) -ADD_PROPERTY_METADATA(absl::optional, TrackOnColor) -ADD_PROPERTY_METADATA(absl::optional, TrackOffColor) END_METADATA } // namespace views diff --git a/ui/views/controls/button/toggle_button.h b/ui/views/controls/button/toggle_button.h index dfccf2204fd69..04f4d6edbee39 100644 --- a/ui/views/controls/button/toggle_button.h +++ b/ui/views/controls/button/toggle_button.h @@ -7,7 +7,9 @@ #include "base/memory/raw_ptr.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/abseil-cpp/absl/types/variant.h" #include "ui/base/metadata/metadata_header_macros.h" +#include "ui/color/color_id.h" #include "ui/gfx/animation/slide_animation.h" #include "ui/views/controls/button/button.h" @@ -25,6 +27,7 @@ class VIEWS_EXPORT ToggleButton : public Button { METADATA_HEADER(ToggleButton); explicit ToggleButton(PressedCallback callback = PressedCallback()); + ToggleButton(PressedCallback callback, bool has_thumb_shadow); ToggleButton(const ToggleButton&) = delete; ToggleButton& operator=(const ToggleButton&) = delete; @@ -36,13 +39,14 @@ class VIEWS_EXPORT ToggleButton : public Button { void SetIsOn(bool is_on); bool GetIsOn() const; - void SetThumbOnColor(const absl::optional& thumb_on_color); + // Sets and gets custom thumb and track colors. + void SetThumbOnColor(SkColor thumb_on_color); absl::optional GetThumbOnColor() const; - void SetThumbOffColor(const absl::optional& thumb_off_color); + void SetThumbOffColor(SkColor thumb_off_color); absl::optional GetThumbOffColor() const; - void SetTrackOnColor(const absl::optional& track_on_color); + void SetTrackOnColor(SkColor track_on_color); absl::optional GetTrackOnColor() const; - void SetTrackOffColor(const absl::optional& track_off_color); + void SetTrackOffColor(SkColor track_off_color); absl::optional GetTrackOffColor() const; void SetAcceptsEvents(bool accepts_events); @@ -62,19 +66,22 @@ class VIEWS_EXPORT ToggleButton : public Button { void StateChanged(ButtonState old_state) override; // Returns the path to draw the focus ring around for this ToggleButton. - SkPath GetFocusRingPath() const; + virtual SkPath GetFocusRingPath() const; + + // Calculates and returns the bounding box for the track. + virtual gfx::Rect GetTrackBounds() const; + + // Calculates and returns the bounding box for the thumb (the circle). + virtual gfx::Rect GetThumbBounds() const; + + // Gets current slide animation progress. + double GetAnimationProgress() const; private: friend class TestToggleButton; class FocusRingHighlightPathGenerator; class ThumbView; - // Calculates and returns the bounding box for the track. - gfx::Rect GetTrackBounds() const; - - // Calculates and returns the bounding box for the thumb (the circle). - gfx::Rect GetThumbBounds() const; - // Updates position of the thumb. void UpdateThumb(); @@ -100,8 +107,10 @@ class VIEWS_EXPORT ToggleButton : public Button { gfx::SlideAnimation slide_animation_{this}; gfx::SlideAnimation hover_animation_{this}; raw_ptr thumb_view_; - absl::optional track_on_color_; - absl::optional track_off_color_; + absl::variant track_on_color_ = + ui::kColorToggleButtonTrackOn; + absl::variant track_off_color_ = + ui::kColorToggleButtonTrackOff; // When false, this button won't accept input. Different from View::SetEnabled // in that the view retains focus when this is false but not when disabled.