Chromium Code Reviews| Index: ui/views/controls/button/toggle_button.cc |
| diff --git a/ui/views/controls/button/toggle_button.cc b/ui/views/controls/button/toggle_button.cc |
| index 30bf562a54887ce6a3b62c24af0408b80104c884..e7c656b641bfaaeb7eee4fe06c47f89abc99c3ea 100644 |
| --- a/ui/views/controls/button/toggle_button.cc |
| +++ b/ui/views/controls/button/toggle_button.cc |
| @@ -34,17 +34,85 @@ const SkColor kTrackOffColor = |
| } // namespace |
| +// Class representing the thumb. When the thumb is clicked it is separated into |
| +// its own layer and the ink drop layer is made a child of the thumb layer |
| +// allowing the two to animate in sync. |
| +class ToggleButton::ThumbView : public views::View { |
| + public: |
| + ThumbView() : color_ratio_(0.) {} |
| + ~ThumbView() override {} |
| + |
| + void AddInkDropLayer(ui::Layer* ink_drop_layer) { |
|
Evan Stade
2016/10/17 15:37:58
if ThumbView were an InkDropHostView could we not
varkha
2016/10/17 16:12:53
Yes, but this would mean we would have to fix the
Evan Stade
2016/10/18 13:13:15
I didn't mean to replace the ToggleButton as the I
|
| + SetPaintToLayer(true); |
| + layer()->SetFillsBoundsOpaquely(false); |
| + layer()->Add(ink_drop_layer); |
| + } |
| + |
| + void RemoveInkDropLayer(ui::Layer* ink_drop_layer) { |
| + layer()->Remove(ink_drop_layer); |
| + SetPaintToLayer(false); |
| + } |
| + |
| + void Update(const gfx::Rect& bounds, double color_ratio) { |
| + SetBoundsRect(bounds); |
| + color_ratio_ = color_ratio; |
| + SchedulePaint(); |
| + } |
| + |
| + private: |
| + // views::View: |
| + const char* GetClassName() const override { |
| + return "ToggleButton::ThumbView"; |
| + } |
| + |
| + void OnPaint(gfx::Canvas* canvas) override { |
| + std::vector<gfx::ShadowValue> shadows; |
| + shadows.emplace_back(gfx::Vector2d(0, 1), 4.f, |
| + SkColorSetA(SK_ColorBLACK, 0x99)); |
| + SkPaint thumb_paint; |
| + thumb_paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); |
| + thumb_paint.setStyle(SkPaint::kFill_Style); |
| + thumb_paint.setAntiAlias(true); |
| + const SkColor thumb_on_color = GetNativeTheme()->GetSystemColor( |
| + ui::NativeTheme::kColorId_ProminentButtonColor); |
| + // TODO(estade): get this color from the theme? |
| + const SkColor thumb_off_color = SK_ColorWHITE; |
| + const SkAlpha blend = static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio_); |
| + thumb_paint.setColor( |
| + color_utils::AlphaBlend(thumb_on_color, thumb_off_color, blend)); |
| + gfx::Rect thumb_bounds = GetLocalBounds(); |
| + thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin)); |
| + canvas->DrawCircle(gfx::RectF(thumb_bounds).CenterPoint(), |
| + thumb_bounds.height() / 2.f, thumb_paint); |
| + } |
| + |
| + // Color ratio between 0 and 1 that controls the thumb color. |
| + double color_ratio_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ThumbView); |
| +}; |
| + |
| +// static |
| +const char ToggleButton::kViewClassName[] = "ToggleButton"; |
| + |
| ToggleButton::ToggleButton(ButtonListener* listener) |
| - : CustomButton(listener), is_on_(false), slide_animation_(this) { |
| + : CustomButton(listener), |
| + is_on_(false), |
| + slide_animation_(this), |
| + thumb_view_(new ToggleButton::ThumbView()) { |
| slide_animation_.SetSlideDuration(80 /* ms */); |
| slide_animation_.SetTweenType(gfx::Tween::LINEAR); |
| SetBorder(Border::CreateEmptyBorder( |
| gfx::Insets(kTrackVerticalMargin, kTrackHorizontalMargin))); |
| + AddChildView(thumb_view_.get()); |
| SetInkDropMode(InkDropMode::ON); |
| set_has_ink_drop_action_on_click(true); |
| } |
| -ToggleButton::~ToggleButton() {} |
| +ToggleButton::~ToggleButton() { |
| + // Destroying ink drop early allows ink drop layer to be properly removed, |
| + SetInkDropMode(InkDropMode::OFF); |
| +} |
| void ToggleButton::SetIsOn(bool is_on, bool animate) { |
| if (is_on_ == is_on) |
| @@ -59,6 +127,24 @@ void ToggleButton::SetIsOn(bool is_on, bool animate) { |
| slide_animation_.Hide(); |
| } |
| +gfx::Rect ToggleButton::GetThumbBounds() const { |
| + gfx::Rect thumb_bounds = GetLocalBounds(); |
| + thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin, kThumbHorizontalMargin)); |
| + thumb_bounds.set_x(thumb_bounds.x() + |
| + slide_animation_.GetCurrentValue() * |
| + (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.set_x(GetMirroredXForRect(thumb_bounds)); |
| + return thumb_bounds; |
| +} |
| + |
| +void ToggleButton::UpdateThumb() { |
| + gfx::Rect thumb_bounds = GetThumbBounds(); |
| + thumb_bounds.Inset(gfx::Insets(-kThumbVerticalMargin)); |
| + thumb_view_->Update(thumb_bounds, slide_animation_.GetCurrentValue()); |
| +} |
| + |
| gfx::Size ToggleButton::GetPreferredSize() const { |
| gfx::Rect rect(0, 0, kTrackWidth, kTrackHeight); |
| if (border()) |
| @@ -66,11 +152,12 @@ gfx::Size ToggleButton::GetPreferredSize() const { |
| return rect.size(); |
| } |
| -void ToggleButton::OnPaint(gfx::Canvas* canvas) { |
| - SkAlpha blend = |
| - static_cast<SkAlpha>(SK_AlphaOPAQUE * slide_animation_.GetCurrentValue()); |
| +const char* ToggleButton::GetClassName() const { |
| + return kViewClassName; |
| +} |
| - // Track. |
| +void ToggleButton::OnPaint(gfx::Canvas* canvas) { |
| + // Paint the toggle track. |
| gfx::RectF track_rect(GetContentsBounds()); |
| SkPaint track_paint; |
| track_paint.setAntiAlias(true); |
| @@ -78,27 +165,11 @@ void ToggleButton::OnPaint(gfx::Canvas* canvas) { |
| SkColorSetA(GetNativeTheme()->GetSystemColor( |
| ui::NativeTheme::kColorId_ProminentButtonColor), |
| 0xFF / 2); |
| - track_paint.setColor( |
| - color_utils::AlphaBlend(track_on_color, kTrackOffColor, blend)); |
| + const double color_ratio = slide_animation_.GetCurrentValue(); |
| + track_paint.setColor(color_utils::AlphaBlend( |
| + track_on_color, kTrackOffColor, |
| + static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio))); |
| canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint); |
| - |
| - // Thumb. |
| - gfx::Rect thumb_bounds = GetThumbBounds(); |
| - SkPaint thumb_paint; |
| - std::vector<gfx::ShadowValue> shadows; |
| - shadows.emplace_back(gfx::Vector2d(0, 1), 4.f, |
| - SkColorSetA(SK_ColorBLACK, 0x99)); |
| - thumb_paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); |
| - thumb_paint.setStyle(SkPaint::kFill_Style); |
| - thumb_paint.setAntiAlias(true); |
| - const SkColor thumb_on_color = GetNativeTheme()->GetSystemColor( |
| - ui::NativeTheme::kColorId_ProminentButtonColor); |
| - // TODO(estade): get this color from the theme? |
| - const SkColor thumb_off_color = SK_ColorWHITE; |
| - thumb_paint.setColor( |
| - color_utils::AlphaBlend(thumb_on_color, thumb_off_color, blend)); |
| - canvas->DrawCircle(gfx::RectF(thumb_bounds).CenterPoint(), |
| - thumb_bounds.height() / 2.f, thumb_paint); |
| } |
| void ToggleButton::NotifyClick(const ui::Event& event) { |
| @@ -106,12 +177,28 @@ void ToggleButton::NotifyClick(const ui::Event& event) { |
| CustomButton::NotifyClick(event); |
| } |
| +void ToggleButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
| + UpdateThumb(); |
| +} |
| + |
| void ToggleButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
| SchedulePaint(); |
| } |
| +void ToggleButton::AddInkDropLayer(ui::Layer* ink_drop_layer) { |
| + thumb_view_->AddInkDropLayer(ink_drop_layer); |
| + UpdateThumb(); |
| + SchedulePaint(); |
| +} |
| + |
| +void ToggleButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { |
| + thumb_view_->RemoveInkDropLayer(ink_drop_layer); |
| + SchedulePaint(); |
| +} |
| + |
| std::unique_ptr<InkDropRipple> ToggleButton::CreateInkDropRipple() const { |
| - return CreateDefaultInkDropRipple(GetThumbBounds().CenterPoint()); |
| + const int radius = (kTrackHeight + kTrackVerticalMargin * 2) / 2; |
| + return CreateDefaultInkDropRipple(gfx::Point(radius, radius)); |
| } |
| SkColor ToggleButton::GetInkDropBaseColor() const { |
| @@ -126,22 +213,14 @@ bool ToggleButton::ShouldShowInkDropHighlight() const { |
| } |
| void ToggleButton::AnimationProgressed(const gfx::Animation* animation) { |
| - if (animation == &slide_animation_) |
| + if (animation == &slide_animation_) { |
| + // TODO(varkha, estade): The thumb is using its own view. Investigate if |
| + // repainting in every animation step to update colors could be avoided. |
|
Evan Stade
2016/10/17 15:37:58
I don't understand this comment. How can we avoid
varkha
2016/10/17 16:12:53
I think sadrul@ was asking if some layer animation
|
| + UpdateThumb(); |
| SchedulePaint(); |
| - else |
| - CustomButton::AnimationProgressed(animation); |
| -} |
| - |
| -gfx::Rect ToggleButton::GetThumbBounds() const { |
| - gfx::Rect thumb_bounds = GetLocalBounds(); |
| - thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin, kThumbHorizontalMargin)); |
| - thumb_bounds.set_x(thumb_bounds.x() + |
| - slide_animation_.GetCurrentValue() * |
| - (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.set_x(GetMirroredXForRect(thumb_bounds)); |
| - return thumb_bounds; |
| + return; |
| + } |
| + CustomButton::AnimationProgressed(animation); |
| } |
| } // namespace views |