Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/controls/button/toggle_button.h" | 5 #include "ui/views/controls/button/toggle_button.h" |
| 6 | 6 |
| 7 #include "third_party/skia/include/core/SkDrawLooper.h" | 7 #include "third_party/skia/include/core/SkDrawLooper.h" |
| 8 #include "third_party/skia/include/core/SkPaint.h" | 8 #include "third_party/skia/include/core/SkPaint.h" |
| 9 #include "ui/gfx/canvas.h" | 9 #include "ui/gfx/canvas.h" |
| 10 #include "ui/gfx/color_palette.h" | 10 #include "ui/gfx/color_palette.h" |
| 11 #include "ui/gfx/color_utils.h" | 11 #include "ui/gfx/color_utils.h" |
| 12 #include "ui/views/animation/ink_drop_ripple.h" | 12 #include "ui/views/animation/ink_drop_ripple.h" |
| 13 #include "ui/views/border.h" | 13 #include "ui/views/border.h" |
| 14 | 14 |
| 15 namespace views { | 15 namespace views { |
| 16 | 16 |
| 17 namespace { | 17 namespace { |
| 18 | 18 |
| 19 // Constants are measured in dip. | 19 // Constants are measured in dip. |
| 20 const int kTrackHeight = 12; | 20 const int kTrackHeight = 12; |
| 21 const int kTrackWidth = 28; | 21 const int kTrackWidth = 28; |
| 22 // Margins from edge of track to edge of view. | 22 // Margins from edge of track to edge of view. |
| 23 const int kTrackVerticalMargin = 5; | 23 const int kTrackVerticalMargin = 4; |
| 24 const int kTrackHorizontalMargin = 6; | 24 const int kTrackHorizontalMargin = 6; |
| 25 // Margin from edge of thumb to closest edge of view. Note that the thumb | 25 // Margin from edge of thumb to closest edge of view. |
| 26 // margins must be sufficiently large to allow space for the shadow. | |
| 27 const int kThumbHorizontalMargin = 4; | 26 const int kThumbHorizontalMargin = 4; |
| 28 // Margin from top/bottom edge of thumb to top/bottom edge of view. | 27 // Margin from top/bottom edge of thumb to top/bottom edge of view. |
| 29 const int kThumbVerticalMargin = 3; | 28 const int kThumbVerticalMargin = 2; |
| 30 | |
| 31 // TODO(estade): get the base color (black) from the theme? | |
| 32 const SkColor kTrackOffColor = | |
| 33 SkColorSetA(SK_ColorBLACK, gfx::kDisabledControlAlpha); | |
| 34 | 29 |
| 35 } // namespace | 30 } // namespace |
| 36 | 31 |
| 37 // Class representing the thumb. When the thumb is clicked it is separated into | 32 // Class representing the thumb (the circle that slides horizontally). |
| 38 // its own layer and the ink drop layer is made a child of the thumb layer | 33 class ToggleButton::ThumbView : public InkDropHostView { |
| 39 // allowing the two to animate in sync. | |
| 40 class ToggleButton::ThumbView : public views::View { | |
| 41 public: | 34 public: |
| 42 ThumbView() : color_ratio_(0.) {} | 35 ThumbView() : color_ratio_(0.) { |
| 43 ~ThumbView() override {} | |
| 44 | |
| 45 void AddInkDropLayer(ui::Layer* ink_drop_layer) { | |
| 46 SetPaintToLayer(true); | 36 SetPaintToLayer(true); |
|
varkha
2016/10/21 00:35:18
The old implementation was more lines but it was o
Evan Stade
2016/10/21 17:19:15
It's not clear to me that creating and destroying
varkha
2016/10/21 17:55:36
+bruthig@ and sadrul@ to see if they have stronger
bruthig
2016/10/21 18:36:40
Even though the ThumbView is always painting to a
sadrul
2016/10/24 14:46:12
Always having the layer seem unrelated to the bugs
Evan Stade
2016/10/27 17:12:14
Done.
| |
| 47 layer()->SetFillsBoundsOpaquely(false); | 37 layer()->SetFillsBoundsOpaquely(false); |
| 48 layer()->Add(ink_drop_layer); | |
| 49 } | 38 } |
| 50 | 39 ~ThumbView() override {} |
| 51 void RemoveInkDropLayer(ui::Layer* ink_drop_layer) { | |
| 52 layer()->Remove(ink_drop_layer); | |
| 53 SetPaintToLayer(false); | |
| 54 } | |
| 55 | 40 |
| 56 void Update(const gfx::Rect& bounds, double color_ratio) { | 41 void Update(const gfx::Rect& bounds, double color_ratio) { |
| 57 SetBoundsRect(bounds); | 42 SetBoundsRect(bounds); |
| 58 color_ratio_ = color_ratio; | 43 color_ratio_ = color_ratio; |
| 59 SchedulePaint(); | 44 SchedulePaint(); |
| 60 } | 45 } |
| 61 | 46 |
| 47 static gfx::Insets GetShadowInsets() { | |
| 48 gfx::Insets blur(kShadowBlur); | |
| 49 gfx::Insets offset(-kShadowOffsetY, -kShadowOffsetX, kShadowOffsetY, | |
| 50 kShadowOffsetX); | |
| 51 return blur + offset; | |
| 52 } | |
| 53 | |
| 62 private: | 54 private: |
| 55 static const int kShadowOffsetX = 0; | |
| 56 static const int kShadowOffsetY = 1; | |
| 57 static const int kShadowBlur = 2; | |
| 58 | |
| 63 // views::View: | 59 // views::View: |
| 64 const char* GetClassName() const override { | 60 const char* GetClassName() const override { |
| 65 return "ToggleButton::ThumbView"; | 61 return "ToggleButton::ThumbView"; |
| 66 } | 62 } |
| 67 | 63 |
| 68 void OnPaint(gfx::Canvas* canvas) override { | 64 void OnPaint(gfx::Canvas* canvas) override { |
| 69 std::vector<gfx::ShadowValue> shadows; | 65 std::vector<gfx::ShadowValue> shadows; |
| 70 shadows.emplace_back(gfx::Vector2d(0, 1), 4.f, | 66 shadows.emplace_back( |
| 71 SkColorSetA(SK_ColorBLACK, 0x99)); | 67 gfx::Vector2d(kShadowOffsetX, kShadowOffsetY), 2 * kShadowBlur, |
| 68 SkColorSetA(GetNativeTheme()->GetSystemColor( | |
| 69 ui::NativeTheme::kColorId_LabelEnabledColor), | |
| 70 0x99)); | |
| 72 SkPaint thumb_paint; | 71 SkPaint thumb_paint; |
| 73 thumb_paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); | 72 thumb_paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); |
| 74 thumb_paint.setStyle(SkPaint::kFill_Style); | |
| 75 thumb_paint.setAntiAlias(true); | 73 thumb_paint.setAntiAlias(true); |
| 76 const SkColor thumb_on_color = GetNativeTheme()->GetSystemColor( | 74 const SkColor thumb_on_color = GetNativeTheme()->GetSystemColor( |
| 77 ui::NativeTheme::kColorId_ProminentButtonColor); | 75 ui::NativeTheme::kColorId_ProminentButtonColor); |
| 78 // TODO(estade): get this color from the theme? | 76 const SkColor thumb_off_color = GetNativeTheme()->GetSystemColor( |
| 79 const SkColor thumb_off_color = SK_ColorWHITE; | 77 ui::NativeTheme::kColorId_DialogBackground); |
| 80 const SkAlpha blend = static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio_); | 78 const SkAlpha blend = static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio_); |
| 81 thumb_paint.setColor( | 79 thumb_paint.setColor( |
| 82 color_utils::AlphaBlend(thumb_on_color, thumb_off_color, blend)); | 80 color_utils::AlphaBlend(thumb_on_color, thumb_off_color, blend)); |
| 83 gfx::Rect thumb_bounds = GetLocalBounds(); | 81 |
| 84 thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin)); | 82 gfx::RectF thumb_bounds(GetLocalBounds()); |
| 85 canvas->DrawCircle(gfx::RectF(thumb_bounds).CenterPoint(), | 83 thumb_bounds.Inset(GetShadowInsets()); |
| 86 thumb_bounds.height() / 2.f, thumb_paint); | 84 thumb_bounds.Inset(gfx::InsetsF(0.5f)); |
| 85 gfx::RectF bounds_in_px = thumb_bounds; | |
| 86 // We want the circle to have an integer pixel diameter and to be aligned | |
| 87 // with pixel boundaries, so we scale dip bounds to pixel bounds, round, and | |
| 88 // unscale back to dip bounds. | |
| 89 bounds_in_px.Scale(canvas->image_scale()); | |
| 90 gfx::RectF bounds_in_dip(gfx::ToEnclosingRect(bounds_in_px)); | |
| 91 bounds_in_dip.Scale(1.f / canvas->image_scale()); | |
| 92 canvas->DrawCircle(bounds_in_dip.CenterPoint(), | |
| 93 bounds_in_dip.height() / 2.f, thumb_paint); | |
| 87 } | 94 } |
| 88 | 95 |
| 89 // Color ratio between 0 and 1 that controls the thumb color. | 96 // Color ratio between 0 and 1 that controls the thumb color. |
| 90 double color_ratio_; | 97 double color_ratio_; |
| 91 | 98 |
| 92 DISALLOW_COPY_AND_ASSIGN(ThumbView); | 99 DISALLOW_COPY_AND_ASSIGN(ThumbView); |
| 93 }; | 100 }; |
| 94 | 101 |
| 95 // static | 102 // static |
| 96 const char ToggleButton::kViewClassName[] = "ToggleButton"; | 103 const char ToggleButton::kViewClassName[] = "ToggleButton"; |
| 97 | 104 |
| 98 ToggleButton::ToggleButton(ButtonListener* listener) | 105 ToggleButton::ToggleButton(ButtonListener* listener) |
| 99 : CustomButton(listener), | 106 : CustomButton(listener), |
| 100 is_on_(false), | 107 is_on_(false), |
| 101 slide_animation_(this), | 108 slide_animation_(this), |
| 102 thumb_view_(new ToggleButton::ThumbView()) { | 109 thumb_view_(new ThumbView()) { |
| 103 slide_animation_.SetSlideDuration(80 /* ms */); | 110 slide_animation_.SetSlideDuration(80 /* ms */); |
| 104 slide_animation_.SetTweenType(gfx::Tween::LINEAR); | 111 slide_animation_.SetTweenType(gfx::Tween::LINEAR); |
| 105 SetBorder(Border::CreateEmptyBorder( | 112 SetBorder(Border::CreateEmptyBorder( |
| 106 gfx::Insets(kTrackVerticalMargin, kTrackHorizontalMargin))); | 113 gfx::Insets(kTrackVerticalMargin, kTrackHorizontalMargin))); |
| 107 AddChildView(thumb_view_.get()); | 114 AddChildView(thumb_view_); |
| 108 SetInkDropMode(InkDropMode::ON); | 115 SetInkDropMode(InkDropMode::ON); |
| 109 set_has_ink_drop_action_on_click(true); | 116 set_has_ink_drop_action_on_click(true); |
| 110 } | 117 } |
| 111 | 118 |
| 112 ToggleButton::~ToggleButton() { | 119 ToggleButton::~ToggleButton() { |
| 113 // Destroying ink drop early allows ink drop layer to be properly removed, | 120 // Destroying ink drop early allows ink drop layer to be properly removed, |
| 114 SetInkDropMode(InkDropMode::OFF); | 121 SetInkDropMode(InkDropMode::OFF); |
| 115 } | 122 } |
| 116 | 123 |
| 117 void ToggleButton::SetIsOn(bool is_on, bool animate) { | 124 void ToggleButton::SetIsOn(bool is_on, bool animate) { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 129 | 136 |
| 130 gfx::Rect ToggleButton::GetThumbBounds() const { | 137 gfx::Rect ToggleButton::GetThumbBounds() const { |
| 131 gfx::Rect thumb_bounds = GetLocalBounds(); | 138 gfx::Rect thumb_bounds = GetLocalBounds(); |
| 132 thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin, kThumbHorizontalMargin)); | 139 thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin, kThumbHorizontalMargin)); |
| 133 thumb_bounds.set_x(thumb_bounds.x() + | 140 thumb_bounds.set_x(thumb_bounds.x() + |
| 134 slide_animation_.GetCurrentValue() * | 141 slide_animation_.GetCurrentValue() * |
| 135 (thumb_bounds.width() - thumb_bounds.height())); | 142 (thumb_bounds.width() - thumb_bounds.height())); |
| 136 // The thumb is a circle, so the width should match the height. | 143 // The thumb is a circle, so the width should match the height. |
| 137 thumb_bounds.set_width(thumb_bounds.height()); | 144 thumb_bounds.set_width(thumb_bounds.height()); |
| 138 thumb_bounds.set_x(GetMirroredXForRect(thumb_bounds)); | 145 thumb_bounds.set_x(GetMirroredXForRect(thumb_bounds)); |
| 146 thumb_bounds.Inset(-ThumbView::GetShadowInsets()); | |
|
varkha
2016/10/21 00:35:18
Thank you!
| |
| 139 return thumb_bounds; | 147 return thumb_bounds; |
| 140 } | 148 } |
| 141 | 149 |
| 142 void ToggleButton::UpdateThumb() { | 150 void ToggleButton::UpdateThumb() { |
| 143 gfx::Rect thumb_bounds = GetThumbBounds(); | 151 thumb_view_->Update(GetThumbBounds(), slide_animation_.GetCurrentValue()); |
| 144 thumb_bounds.Inset(gfx::Insets(-kThumbVerticalMargin)); | |
| 145 thumb_view_->Update(thumb_bounds, slide_animation_.GetCurrentValue()); | |
| 146 } | 152 } |
| 147 | 153 |
| 148 gfx::Size ToggleButton::GetPreferredSize() const { | 154 gfx::Size ToggleButton::GetPreferredSize() const { |
| 149 gfx::Rect rect(0, 0, kTrackWidth, kTrackHeight); | 155 gfx::Rect rect(0, 0, kTrackWidth, kTrackHeight); |
| 150 if (border()) | 156 if (border()) |
| 151 rect.Inset(-border()->GetInsets()); | 157 rect.Inset(-border()->GetInsets()); |
| 152 return rect.size(); | 158 return rect.size(); |
| 153 } | 159 } |
| 154 | 160 |
| 155 const char* ToggleButton::GetClassName() const { | 161 const char* ToggleButton::GetClassName() const { |
| 156 return kViewClassName; | 162 return kViewClassName; |
| 157 } | 163 } |
| 158 | 164 |
| 159 void ToggleButton::OnPaint(gfx::Canvas* canvas) { | 165 void ToggleButton::OnPaint(gfx::Canvas* canvas) { |
| 160 // Paint the toggle track. | 166 // Paint the toggle track. |
| 161 gfx::RectF track_rect(GetContentsBounds()); | 167 gfx::RectF track_rect(GetContentsBounds()); |
| 162 SkPaint track_paint; | 168 SkPaint track_paint; |
| 163 track_paint.setAntiAlias(true); | 169 track_paint.setAntiAlias(true); |
| 164 const SkColor track_on_color = | |
| 165 SkColorSetA(GetNativeTheme()->GetSystemColor( | |
| 166 ui::NativeTheme::kColorId_ProminentButtonColor), | |
| 167 0xFF / 2); | |
| 168 const double color_ratio = slide_animation_.GetCurrentValue(); | 170 const double color_ratio = slide_animation_.GetCurrentValue(); |
| 169 track_paint.setColor(color_utils::AlphaBlend( | 171 track_paint.setColor(color_utils::AlphaBlend( |
| 170 track_on_color, kTrackOffColor, | 172 GetTrackColor(true), GetTrackColor(false), |
| 171 static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio))); | 173 static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio))); |
| 172 canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint); | 174 canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint); |
| 173 } | 175 } |
| 174 | 176 |
| 175 void ToggleButton::NotifyClick(const ui::Event& event) { | 177 void ToggleButton::NotifyClick(const ui::Event& event) { |
| 176 SetIsOn(!is_on(), true); | 178 SetIsOn(!is_on(), true); |
| 177 CustomButton::NotifyClick(event); | 179 CustomButton::NotifyClick(event); |
| 178 } | 180 } |
| 179 | 181 |
| 180 void ToggleButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { | 182 void ToggleButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
| 181 UpdateThumb(); | 183 UpdateThumb(); |
| 182 } | 184 } |
| 183 | 185 |
| 184 void ToggleButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { | 186 void ToggleButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
| 185 SchedulePaint(); | 187 SchedulePaint(); |
| 186 } | 188 } |
| 187 | 189 |
| 188 void ToggleButton::AddInkDropLayer(ui::Layer* ink_drop_layer) { | 190 void ToggleButton::AddInkDropLayer(ui::Layer* ink_drop_layer) { |
| 189 thumb_view_->AddInkDropLayer(ink_drop_layer); | 191 thumb_view_->AddInkDropLayer(ink_drop_layer); |
| 190 UpdateThumb(); | 192 UpdateThumb(); |
| 191 SchedulePaint(); | 193 SchedulePaint(); |
|
bruthig
2016/10/21 18:36:40
varkha@, IF the ThumbView is always painting to a
| |
| 192 } | 194 } |
| 193 | 195 |
| 194 void ToggleButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { | 196 void ToggleButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { |
| 195 thumb_view_->RemoveInkDropLayer(ink_drop_layer); | 197 thumb_view_->RemoveInkDropLayer(ink_drop_layer); |
| 196 SchedulePaint(); | 198 SchedulePaint(); |
| 197 } | 199 } |
| 198 | 200 |
| 199 std::unique_ptr<InkDropRipple> ToggleButton::CreateInkDropRipple() const { | 201 std::unique_ptr<InkDropRipple> ToggleButton::CreateInkDropRipple() const { |
| 200 const int radius = (kTrackHeight + kTrackVerticalMargin * 2) / 2; | 202 const int radius = (kTrackHeight + kTrackVerticalMargin * 2) / 2; |
| 201 return CreateDefaultInkDropRipple(gfx::Point(radius, radius)); | 203 return CreateDefaultInkDropRipple(gfx::Point(radius, radius)); |
| 202 } | 204 } |
| 203 | 205 |
| 204 SkColor ToggleButton::GetInkDropBaseColor() const { | 206 SkColor ToggleButton::GetInkDropBaseColor() const { |
| 205 return is_on() | 207 return GetTrackColor(is_on()); |
| 206 ? GetNativeTheme()->GetSystemColor( | |
| 207 ui::NativeTheme::kColorId_ProminentButtonColor) | |
| 208 : kTrackOffColor; | |
| 209 } | 208 } |
| 210 | 209 |
| 211 bool ToggleButton::ShouldShowInkDropHighlight() const { | 210 bool ToggleButton::ShouldShowInkDropHighlight() const { |
| 212 return false; | 211 return false; |
| 213 } | 212 } |
| 214 | 213 |
| 215 void ToggleButton::AnimationProgressed(const gfx::Animation* animation) { | 214 void ToggleButton::AnimationProgressed(const gfx::Animation* animation) { |
| 216 if (animation == &slide_animation_) { | 215 if (animation == &slide_animation_) { |
| 217 // TODO(varkha, estade): The thumb is using its own view. Investigate if | 216 // TODO(varkha, estade): The thumb is using its own view. Investigate if |
| 218 // repainting in every animation step to update colors could be avoided. | 217 // repainting in every animation step to update colors could be avoided. |
| 219 UpdateThumb(); | 218 UpdateThumb(); |
| 220 SchedulePaint(); | 219 SchedulePaint(); |
| 221 return; | 220 return; |
| 222 } | 221 } |
| 223 CustomButton::AnimationProgressed(animation); | 222 CustomButton::AnimationProgressed(animation); |
| 224 } | 223 } |
| 225 | 224 |
| 225 SkColor ToggleButton::GetTrackColor(bool is_on) const { | |
| 226 const SkAlpha kOffTrackAlpha = 0x29; | |
| 227 const SkAlpha kOnTrackAlpha = kOffTrackAlpha * 2; | |
| 228 ui::NativeTheme::ColorId color_id = | |
| 229 is_on ? ui::NativeTheme::kColorId_ProminentButtonColor | |
| 230 : ui::NativeTheme::kColorId_LabelEnabledColor; | |
| 231 return SkColorSetA(GetNativeTheme()->GetSystemColor(color_id), | |
| 232 is_on ? kOnTrackAlpha : kOffTrackAlpha); | |
| 233 } | |
| 234 | |
| 226 } // namespace views | 235 } // namespace views |
| OLD | NEW |