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" |
(...skipping 16 matching lines...) Expand all Loading... | |
27 const int kThumbHorizontalMargin = 4; | 27 const int kThumbHorizontalMargin = 4; |
28 // Margin from top/bottom edge of thumb to top/bottom edge of view. | 28 // Margin from top/bottom edge of thumb to top/bottom edge of view. |
29 const int kThumbVerticalMargin = 3; | 29 const int kThumbVerticalMargin = 3; |
30 | 30 |
31 // TODO(estade): get the base color (black) from the theme? | 31 // TODO(estade): get the base color (black) from the theme? |
32 const SkColor kTrackOffColor = | 32 const SkColor kTrackOffColor = |
33 SkColorSetA(SK_ColorBLACK, gfx::kDisabledControlAlpha); | 33 SkColorSetA(SK_ColorBLACK, gfx::kDisabledControlAlpha); |
34 | 34 |
35 } // namespace | 35 } // namespace |
36 | 36 |
37 // Class representing the thumb. When the thumb is clicked it is separated into | |
38 // its own layer and the ink drop layer is made a child of the thumb layer | |
39 // allowing the two to animate in sync. | |
40 class ToggleButton::ThumbView : public views::View { | |
41 public: | |
42 ThumbView() : color_ratio_(0.) {} | |
43 ~ThumbView() override {} | |
44 | |
45 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
| |
46 SetPaintToLayer(true); | |
47 layer()->SetFillsBoundsOpaquely(false); | |
48 layer()->Add(ink_drop_layer); | |
49 } | |
50 | |
51 void RemoveInkDropLayer(ui::Layer* ink_drop_layer) { | |
52 layer()->Remove(ink_drop_layer); | |
53 SetPaintToLayer(false); | |
54 } | |
55 | |
56 void Update(const gfx::Rect& bounds, double color_ratio) { | |
57 SetBoundsRect(bounds); | |
58 color_ratio_ = color_ratio; | |
59 SchedulePaint(); | |
60 } | |
61 | |
62 private: | |
63 // views::View: | |
64 const char* GetClassName() const override { | |
65 return "ToggleButton::ThumbView"; | |
66 } | |
67 | |
68 void OnPaint(gfx::Canvas* canvas) override { | |
69 std::vector<gfx::ShadowValue> shadows; | |
70 shadows.emplace_back(gfx::Vector2d(0, 1), 4.f, | |
71 SkColorSetA(SK_ColorBLACK, 0x99)); | |
72 SkPaint thumb_paint; | |
73 thumb_paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); | |
74 thumb_paint.setStyle(SkPaint::kFill_Style); | |
75 thumb_paint.setAntiAlias(true); | |
76 const SkColor thumb_on_color = GetNativeTheme()->GetSystemColor( | |
77 ui::NativeTheme::kColorId_ProminentButtonColor); | |
78 // TODO(estade): get this color from the theme? | |
79 const SkColor thumb_off_color = SK_ColorWHITE; | |
80 const SkAlpha blend = static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio_); | |
81 thumb_paint.setColor( | |
82 color_utils::AlphaBlend(thumb_on_color, thumb_off_color, blend)); | |
83 gfx::Rect thumb_bounds = GetLocalBounds(); | |
84 thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin)); | |
85 canvas->DrawCircle(gfx::RectF(thumb_bounds).CenterPoint(), | |
86 thumb_bounds.height() / 2.f, thumb_paint); | |
87 } | |
88 | |
89 // Color ratio between 0 and 1 that controls the thumb color. | |
90 double color_ratio_; | |
91 | |
92 DISALLOW_COPY_AND_ASSIGN(ThumbView); | |
93 }; | |
94 | |
95 // static | |
96 const char ToggleButton::kViewClassName[] = "ToggleButton"; | |
97 | |
37 ToggleButton::ToggleButton(ButtonListener* listener) | 98 ToggleButton::ToggleButton(ButtonListener* listener) |
38 : CustomButton(listener), is_on_(false), slide_animation_(this) { | 99 : CustomButton(listener), |
100 is_on_(false), | |
101 slide_animation_(this), | |
102 thumb_view_(new ToggleButton::ThumbView()) { | |
39 slide_animation_.SetSlideDuration(80 /* ms */); | 103 slide_animation_.SetSlideDuration(80 /* ms */); |
40 slide_animation_.SetTweenType(gfx::Tween::LINEAR); | 104 slide_animation_.SetTweenType(gfx::Tween::LINEAR); |
41 SetBorder(Border::CreateEmptyBorder( | 105 SetBorder(Border::CreateEmptyBorder( |
42 gfx::Insets(kTrackVerticalMargin, kTrackHorizontalMargin))); | 106 gfx::Insets(kTrackVerticalMargin, kTrackHorizontalMargin))); |
107 AddChildView(thumb_view_.get()); | |
43 SetInkDropMode(InkDropMode::ON); | 108 SetInkDropMode(InkDropMode::ON); |
44 set_has_ink_drop_action_on_click(true); | 109 set_has_ink_drop_action_on_click(true); |
45 } | 110 } |
46 | 111 |
47 ToggleButton::~ToggleButton() {} | 112 ToggleButton::~ToggleButton() { |
113 // Destroying ink drop early allows ink drop layer to be properly removed, | |
114 SetInkDropMode(InkDropMode::OFF); | |
115 } | |
48 | 116 |
49 void ToggleButton::SetIsOn(bool is_on, bool animate) { | 117 void ToggleButton::SetIsOn(bool is_on, bool animate) { |
50 if (is_on_ == is_on) | 118 if (is_on_ == is_on) |
51 return; | 119 return; |
52 | 120 |
53 is_on_ = is_on; | 121 is_on_ = is_on; |
54 if (!animate) | 122 if (!animate) |
55 slide_animation_.Reset(is_on_ ? 1.0 : 0.0); | 123 slide_animation_.Reset(is_on_ ? 1.0 : 0.0); |
56 else if (is_on_) | 124 else if (is_on_) |
57 slide_animation_.Show(); | 125 slide_animation_.Show(); |
58 else | 126 else |
59 slide_animation_.Hide(); | 127 slide_animation_.Hide(); |
60 } | 128 } |
61 | 129 |
130 gfx::Rect ToggleButton::GetThumbBounds() const { | |
131 gfx::Rect thumb_bounds = GetLocalBounds(); | |
132 thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin, kThumbHorizontalMargin)); | |
133 thumb_bounds.set_x(thumb_bounds.x() + | |
134 slide_animation_.GetCurrentValue() * | |
135 (thumb_bounds.width() - thumb_bounds.height())); | |
136 // The thumb is a circle, so the width should match the height. | |
137 thumb_bounds.set_width(thumb_bounds.height()); | |
138 thumb_bounds.set_x(GetMirroredXForRect(thumb_bounds)); | |
139 return thumb_bounds; | |
140 } | |
141 | |
142 void ToggleButton::UpdateThumb() { | |
143 gfx::Rect thumb_bounds = GetThumbBounds(); | |
144 thumb_bounds.Inset(gfx::Insets(-kThumbVerticalMargin)); | |
145 thumb_view_->Update(thumb_bounds, slide_animation_.GetCurrentValue()); | |
146 } | |
147 | |
62 gfx::Size ToggleButton::GetPreferredSize() const { | 148 gfx::Size ToggleButton::GetPreferredSize() const { |
63 gfx::Rect rect(0, 0, kTrackWidth, kTrackHeight); | 149 gfx::Rect rect(0, 0, kTrackWidth, kTrackHeight); |
64 if (border()) | 150 if (border()) |
65 rect.Inset(-border()->GetInsets()); | 151 rect.Inset(-border()->GetInsets()); |
66 return rect.size(); | 152 return rect.size(); |
67 } | 153 } |
68 | 154 |
155 const char* ToggleButton::GetClassName() const { | |
156 return kViewClassName; | |
157 } | |
158 | |
69 void ToggleButton::OnPaint(gfx::Canvas* canvas) { | 159 void ToggleButton::OnPaint(gfx::Canvas* canvas) { |
70 SkAlpha blend = | 160 // Paint the toggle track. |
71 static_cast<SkAlpha>(SK_AlphaOPAQUE * slide_animation_.GetCurrentValue()); | |
72 | |
73 // Track. | |
74 gfx::RectF track_rect(GetContentsBounds()); | 161 gfx::RectF track_rect(GetContentsBounds()); |
75 SkPaint track_paint; | 162 SkPaint track_paint; |
76 track_paint.setAntiAlias(true); | 163 track_paint.setAntiAlias(true); |
77 const SkColor track_on_color = | 164 const SkColor track_on_color = |
78 SkColorSetA(GetNativeTheme()->GetSystemColor( | 165 SkColorSetA(GetNativeTheme()->GetSystemColor( |
79 ui::NativeTheme::kColorId_ProminentButtonColor), | 166 ui::NativeTheme::kColorId_ProminentButtonColor), |
80 0xFF / 2); | 167 0xFF / 2); |
81 track_paint.setColor( | 168 const double color_ratio = slide_animation_.GetCurrentValue(); |
82 color_utils::AlphaBlend(track_on_color, kTrackOffColor, blend)); | 169 track_paint.setColor(color_utils::AlphaBlend( |
170 track_on_color, kTrackOffColor, | |
171 static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio))); | |
83 canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint); | 172 canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint); |
84 | |
85 // Thumb. | |
86 gfx::Rect thumb_bounds = GetThumbBounds(); | |
87 SkPaint thumb_paint; | |
88 std::vector<gfx::ShadowValue> shadows; | |
89 shadows.emplace_back(gfx::Vector2d(0, 1), 4.f, | |
90 SkColorSetA(SK_ColorBLACK, 0x99)); | |
91 thumb_paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows)); | |
92 thumb_paint.setStyle(SkPaint::kFill_Style); | |
93 thumb_paint.setAntiAlias(true); | |
94 const SkColor thumb_on_color = GetNativeTheme()->GetSystemColor( | |
95 ui::NativeTheme::kColorId_ProminentButtonColor); | |
96 // TODO(estade): get this color from the theme? | |
97 const SkColor thumb_off_color = SK_ColorWHITE; | |
98 thumb_paint.setColor( | |
99 color_utils::AlphaBlend(thumb_on_color, thumb_off_color, blend)); | |
100 canvas->DrawCircle(gfx::RectF(thumb_bounds).CenterPoint(), | |
101 thumb_bounds.height() / 2.f, thumb_paint); | |
102 } | 173 } |
103 | 174 |
104 void ToggleButton::NotifyClick(const ui::Event& event) { | 175 void ToggleButton::NotifyClick(const ui::Event& event) { |
105 SetIsOn(!is_on(), true); | 176 SetIsOn(!is_on(), true); |
106 CustomButton::NotifyClick(event); | 177 CustomButton::NotifyClick(event); |
107 } | 178 } |
108 | 179 |
180 void ToggleButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { | |
181 UpdateThumb(); | |
182 } | |
183 | |
109 void ToggleButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { | 184 void ToggleButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
110 SchedulePaint(); | 185 SchedulePaint(); |
111 } | 186 } |
112 | 187 |
188 void ToggleButton::AddInkDropLayer(ui::Layer* ink_drop_layer) { | |
189 thumb_view_->AddInkDropLayer(ink_drop_layer); | |
190 UpdateThumb(); | |
191 SchedulePaint(); | |
192 } | |
193 | |
194 void ToggleButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { | |
195 thumb_view_->RemoveInkDropLayer(ink_drop_layer); | |
196 SchedulePaint(); | |
197 } | |
198 | |
113 std::unique_ptr<InkDropRipple> ToggleButton::CreateInkDropRipple() const { | 199 std::unique_ptr<InkDropRipple> ToggleButton::CreateInkDropRipple() const { |
114 return CreateDefaultInkDropRipple(GetThumbBounds().CenterPoint()); | 200 const int radius = (kTrackHeight + kTrackVerticalMargin * 2) / 2; |
201 return CreateDefaultInkDropRipple(gfx::Point(radius, radius)); | |
115 } | 202 } |
116 | 203 |
117 SkColor ToggleButton::GetInkDropBaseColor() const { | 204 SkColor ToggleButton::GetInkDropBaseColor() const { |
118 return is_on() | 205 return is_on() |
119 ? GetNativeTheme()->GetSystemColor( | 206 ? GetNativeTheme()->GetSystemColor( |
120 ui::NativeTheme::kColorId_ProminentButtonColor) | 207 ui::NativeTheme::kColorId_ProminentButtonColor) |
121 : kTrackOffColor; | 208 : kTrackOffColor; |
122 } | 209 } |
123 | 210 |
124 bool ToggleButton::ShouldShowInkDropHighlight() const { | 211 bool ToggleButton::ShouldShowInkDropHighlight() const { |
125 return false; | 212 return false; |
126 } | 213 } |
127 | 214 |
128 void ToggleButton::AnimationProgressed(const gfx::Animation* animation) { | 215 void ToggleButton::AnimationProgressed(const gfx::Animation* animation) { |
129 if (animation == &slide_animation_) | 216 if (animation == &slide_animation_) { |
217 // TODO(varkha, estade): The thumb is using its own view. Investigate if | |
218 // 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
| |
219 UpdateThumb(); | |
130 SchedulePaint(); | 220 SchedulePaint(); |
131 else | 221 return; |
132 CustomButton::AnimationProgressed(animation); | 222 } |
133 } | 223 CustomButton::AnimationProgressed(animation); |
134 | |
135 gfx::Rect ToggleButton::GetThumbBounds() const { | |
136 gfx::Rect thumb_bounds = GetLocalBounds(); | |
137 thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin, kThumbHorizontalMargin)); | |
138 thumb_bounds.set_x(thumb_bounds.x() + | |
139 slide_animation_.GetCurrentValue() * | |
140 (thumb_bounds.width() - thumb_bounds.height())); | |
141 // The thumb is a circle, so the width should match the height. | |
142 thumb_bounds.set_width(thumb_bounds.height()); | |
143 thumb_bounds.set_x(GetMirroredXForRect(thumb_bounds)); | |
144 return thumb_bounds; | |
145 } | 224 } |
146 | 225 |
147 } // namespace views | 226 } // namespace views |
OLD | NEW |