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/accessibility/ax_node_data.h" | 9 #include "ui/accessibility/ax_node_data.h" |
| 10 #include "ui/gfx/canvas.h" | 10 #include "ui/gfx/canvas.h" |
| 11 #include "ui/gfx/color_palette.h" | 11 #include "ui/gfx/color_palette.h" |
| 12 #include "ui/gfx/color_utils.h" | 12 #include "ui/gfx/color_utils.h" |
| 13 #include "ui/gfx/scoped_canvas.h" | |
| 13 #include "ui/views/animation/ink_drop_impl.h" | 14 #include "ui/views/animation/ink_drop_impl.h" |
| 14 #include "ui/views/animation/ink_drop_ripple.h" | 15 #include "ui/views/animation/ink_drop_ripple.h" |
| 15 #include "ui/views/border.h" | 16 #include "ui/views/border.h" |
| 17 #include "ui/views/painter.h" | |
| 16 | 18 |
| 17 namespace views { | 19 namespace views { |
| 18 | 20 |
| 19 namespace { | 21 namespace { |
| 20 | 22 |
| 21 // Constants are measured in dip. | 23 // Constants are measured in dip. |
| 22 const int kTrackHeight = 12; | 24 const int kTrackHeight = 12; |
| 23 const int kTrackWidth = 28; | 25 const int kTrackWidth = 28; |
| 24 // Margins from edge of track to edge of view. | 26 // Margins from edge of track to edge of view. |
| 25 const int kTrackVerticalMargin = 5; | 27 const int kTrackVerticalMargin = 5; |
| 26 const int kTrackHorizontalMargin = 6; | 28 const int kTrackHorizontalMargin = 6; |
| 27 // Margin from edge of thumb to closest edge of view. Note that the thumb | 29 // Inset from the rounded edge of the thumb to the rounded edge of the track. |
| 28 // margins must be sufficiently large to allow space for the shadow. | 30 const int kThumbInset = 2; |
| 29 const int kThumbHorizontalMargin = 4; | |
| 30 // Margin from top/bottom edge of thumb to top/bottom edge of view. | |
| 31 const int kThumbVerticalMargin = 3; | |
| 32 | 31 |
| 33 } // namespace | 32 } // namespace |
| 34 | 33 |
| 35 // Class representing the thumb (the circle that slides horizontally). | 34 // Class representing the thumb (the circle that slides horizontally). |
| 36 class ToggleButton::ThumbView : public InkDropHostView { | 35 class ToggleButton::ThumbView : public InkDropHostView { |
| 37 public: | 36 public: |
| 38 ThumbView() : color_ratio_(0.) {} | 37 ThumbView(ToggleButton* parent) : color_ratio_(0.), parent_(parent) {} |
| 39 ~ThumbView() override {} | 38 ~ThumbView() override {} |
| 40 | 39 |
| 41 void Update(const gfx::Rect& bounds, double color_ratio) { | 40 void Update(const gfx::Rect& bounds, double color_ratio) { |
| 42 SetBoundsRect(bounds); | 41 SetBoundsRect(bounds); |
| 43 color_ratio_ = color_ratio; | 42 color_ratio_ = color_ratio; |
| 44 SchedulePaint(); | 43 SchedulePaint(); |
| 45 } | 44 } |
| 46 | 45 |
| 47 // Returns the extra space needed to draw the shadows around the thumb. Since | 46 // Returns the extra space needed to draw the shadows around the thumb. Since |
| 48 // the extra space is around the thumb, the insets will be negative. | 47 // the extra space is around the thumb, the insets will be negative. |
| 49 static gfx::Insets GetShadowOutsets() { | 48 static gfx::Insets GetShadowOutsets() { |
| 50 return gfx::Insets(-kShadowBlur) | 49 return gfx::Insets(-kShadowBlur) |
| 51 .Offset(gfx::Vector2d(kShadowOffsetX, kShadowOffsetY)); | 50 .Offset(gfx::Vector2d(kShadowOffsetX, kShadowOffsetY)); |
| 52 } | 51 } |
| 53 | 52 |
| 53 // views::View: | |
| 54 bool GetTooltipText(const gfx::Point& p, | |
| 55 base::string16* tooltip) const override { | |
| 56 return parent_->GetTooltipText(p, tooltip); | |
|
Evan Stade
2016/11/17 21:11:26
can't we just make this view invisible in terms of
varkha
2016/11/17 22:24:57
Yes, TIL. Done.
| |
| 57 } | |
| 58 | |
| 54 private: | 59 private: |
| 55 static const int kShadowOffsetX = 0; | 60 static const int kShadowOffsetX = 0; |
| 56 static const int kShadowOffsetY = 1; | 61 static const int kShadowOffsetY = 1; |
| 57 static const int kShadowBlur = 2; | 62 static const int kShadowBlur = 2; |
| 58 | 63 |
| 59 // views::View: | 64 // views::View: |
| 60 const char* GetClassName() const override { | 65 const char* GetClassName() const override { |
| 61 return "ToggleButton::ThumbView"; | 66 return "ToggleButton::ThumbView"; |
| 62 } | 67 } |
| 63 | 68 |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 88 thumb_bounds.Inset(gfx::InsetsF(0.5f)); | 93 thumb_bounds.Inset(gfx::InsetsF(0.5f)); |
| 89 thumb_bounds.Scale(dsf); | 94 thumb_bounds.Scale(dsf); |
| 90 thumb_bounds = gfx::RectF(gfx::ToEnclosingRect(thumb_bounds)); | 95 thumb_bounds = gfx::RectF(gfx::ToEnclosingRect(thumb_bounds)); |
| 91 canvas->DrawCircle(thumb_bounds.CenterPoint(), thumb_bounds.height() / 2.f, | 96 canvas->DrawCircle(thumb_bounds.CenterPoint(), thumb_bounds.height() / 2.f, |
| 92 thumb_paint); | 97 thumb_paint); |
| 93 } | 98 } |
| 94 | 99 |
| 95 // Color ratio between 0 and 1 that controls the thumb color. | 100 // Color ratio between 0 and 1 that controls the thumb color. |
| 96 double color_ratio_; | 101 double color_ratio_; |
| 97 | 102 |
| 103 // Our parent. | |
| 104 ToggleButton* parent_; | |
|
Evan Stade
2016/11/17 21:11:26
I don't think you need this, just call parent() (a
varkha
2016/11/17 22:24:57
Done.
| |
| 105 | |
| 98 DISALLOW_COPY_AND_ASSIGN(ThumbView); | 106 DISALLOW_COPY_AND_ASSIGN(ThumbView); |
| 99 }; | 107 }; |
| 100 | 108 |
| 101 // static | 109 // static |
| 102 const char ToggleButton::kViewClassName[] = "ToggleButton"; | 110 const char ToggleButton::kViewClassName[] = "ToggleButton"; |
| 103 | 111 |
| 104 ToggleButton::ToggleButton(ButtonListener* listener) | 112 ToggleButton::ToggleButton(ButtonListener* listener) |
| 105 : CustomButton(listener), | 113 : CustomButton(listener), |
| 106 is_on_(false), | 114 is_on_(false), |
| 107 slide_animation_(this), | 115 slide_animation_(this), |
| 108 thumb_view_(new ThumbView()) { | 116 thumb_view_(new ThumbView(this)), |
| 117 focus_painter_(Painter::CreateDashedFocusPainter()) { | |
|
Evan Stade
2016/11/17 21:11:26
I don't think we'll ever want a dashed focus paint
varkha
2016/11/17 22:24:57
Done.
| |
| 109 slide_animation_.SetSlideDuration(80 /* ms */); | 118 slide_animation_.SetSlideDuration(80 /* ms */); |
| 110 slide_animation_.SetTweenType(gfx::Tween::LINEAR); | 119 slide_animation_.SetTweenType(gfx::Tween::LINEAR); |
| 111 SetBorder(CreateEmptyBorder( | |
| 112 gfx::Insets(kTrackVerticalMargin, kTrackHorizontalMargin))); | |
| 113 AddChildView(thumb_view_); | 120 AddChildView(thumb_view_); |
| 114 SetInkDropMode(InkDropMode::ON); | 121 SetInkDropMode(InkDropMode::ON); |
| 115 set_has_ink_drop_action_on_click(true); | 122 set_has_ink_drop_action_on_click(true); |
| 116 } | 123 } |
| 117 | 124 |
| 118 ToggleButton::~ToggleButton() { | 125 ToggleButton::~ToggleButton() { |
| 119 // Destroying ink drop early allows ink drop layer to be properly removed, | 126 // Destroying ink drop early allows ink drop layer to be properly removed, |
| 120 SetInkDropMode(InkDropMode::OFF); | 127 SetInkDropMode(InkDropMode::OFF); |
| 121 } | 128 } |
| 122 | 129 |
| 123 void ToggleButton::SetIsOn(bool is_on, bool animate) { | 130 void ToggleButton::SetIsOn(bool is_on, bool animate) { |
| 124 if (is_on_ == is_on) | 131 if (is_on_ == is_on) |
| 125 return; | 132 return; |
| 126 | 133 |
| 127 is_on_ = is_on; | 134 is_on_ = is_on; |
| 128 if (!animate) { | 135 if (!animate) { |
| 129 slide_animation_.Reset(is_on_ ? 1.0 : 0.0); | 136 slide_animation_.Reset(is_on_ ? 1.0 : 0.0); |
| 130 UpdateThumb(); | 137 UpdateThumb(); |
| 131 SchedulePaint(); | 138 SchedulePaint(); |
| 132 } else if (is_on_) { | 139 } else if (is_on_) { |
| 133 slide_animation_.Show(); | 140 slide_animation_.Show(); |
| 134 } else { | 141 } else { |
| 135 slide_animation_.Hide(); | 142 slide_animation_.Hide(); |
| 136 } | 143 } |
| 137 } | 144 } |
| 138 | 145 |
| 146 void ToggleButton::SetFocusPainter(std::unique_ptr<Painter> focus_painter) { | |
|
Evan Stade
2016/11/17 21:11:26
set_focus_painter
varkha
2016/11/17 22:24:57
Done.
| |
| 147 focus_painter_ = std::move(focus_painter); | |
| 148 } | |
| 149 | |
| 150 void ToggleButton::OnFocus() { | |
| 151 CustomButton::OnFocus(); | |
| 152 if (focus_painter_.get()) | |
| 153 SchedulePaint(); | |
| 154 } | |
| 155 | |
| 156 void ToggleButton::OnBlur() { | |
| 157 CustomButton::OnBlur(); | |
| 158 if (focus_painter_.get()) | |
|
Evan Stade
2016/11/17 21:11:26
.get() shouldn't be necessary
varkha
2016/11/17 22:24:57
Done.
| |
| 159 SchedulePaint(); | |
| 160 } | |
| 161 | |
| 139 gfx::Rect ToggleButton::GetThumbBounds() const { | 162 gfx::Rect ToggleButton::GetThumbBounds() const { |
| 140 gfx::Rect thumb_bounds = GetLocalBounds(); | 163 gfx::Rect thumb_bounds((width() - kTrackWidth) / 2, |
| 141 thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin, kThumbHorizontalMargin)); | 164 (height() - kTrackHeight) / 2, kTrackWidth, |
|
Evan Stade
2016/11/17 21:11:26
please share the track rect calculations
varkha
2016/11/17 22:24:57
Done.
| |
| 165 kTrackHeight); | |
| 166 thumb_bounds.Inset(gfx::Insets(-kThumbInset)); | |
| 142 thumb_bounds.set_x(thumb_bounds.x() + | 167 thumb_bounds.set_x(thumb_bounds.x() + |
| 143 slide_animation_.GetCurrentValue() * | 168 slide_animation_.GetCurrentValue() * |
| 144 (thumb_bounds.width() - thumb_bounds.height())); | 169 (thumb_bounds.width() - thumb_bounds.height())); |
| 145 // The thumb is a circle, so the width should match the height. | 170 // The thumb is a circle, so the width should match the height. |
| 146 thumb_bounds.set_width(thumb_bounds.height()); | 171 thumb_bounds.set_width(thumb_bounds.height()); |
| 147 thumb_bounds.set_x(GetMirroredXForRect(thumb_bounds)); | 172 thumb_bounds.set_x(GetMirroredXForRect(thumb_bounds)); |
| 148 thumb_bounds.Inset(ThumbView::GetShadowOutsets()); | 173 thumb_bounds.Inset(ThumbView::GetShadowOutsets()); |
| 149 return thumb_bounds; | 174 return thumb_bounds; |
| 150 } | 175 } |
| 151 | 176 |
| 152 void ToggleButton::UpdateThumb() { | 177 void ToggleButton::UpdateThumb() { |
| 153 thumb_view_->Update(GetThumbBounds(), slide_animation_.GetCurrentValue()); | 178 thumb_view_->Update(GetThumbBounds(), slide_animation_.GetCurrentValue()); |
| 154 } | 179 } |
| 155 | 180 |
| 156 SkColor ToggleButton::GetTrackColor(bool is_on) const { | 181 SkColor ToggleButton::GetTrackColor(bool is_on) const { |
| 157 const SkAlpha kOffTrackAlpha = 0x29; | 182 const SkAlpha kOffTrackAlpha = 0x29; |
| 158 const SkAlpha kOnTrackAlpha = kOffTrackAlpha * 2; | 183 const SkAlpha kOnTrackAlpha = kOffTrackAlpha * 2; |
| 159 ui::NativeTheme::ColorId color_id = | 184 ui::NativeTheme::ColorId color_id = |
| 160 is_on ? ui::NativeTheme::kColorId_ProminentButtonColor | 185 is_on ? ui::NativeTheme::kColorId_ProminentButtonColor |
| 161 : ui::NativeTheme::kColorId_LabelEnabledColor; | 186 : ui::NativeTheme::kColorId_LabelEnabledColor; |
| 162 return SkColorSetA(GetNativeTheme()->GetSystemColor(color_id), | 187 return SkColorSetA(GetNativeTheme()->GetSystemColor(color_id), |
| 163 is_on ? kOnTrackAlpha : kOffTrackAlpha); | 188 is_on ? kOnTrackAlpha : kOffTrackAlpha); |
| 164 } | 189 } |
| 165 | 190 |
| 166 gfx::Size ToggleButton::GetPreferredSize() const { | 191 gfx::Size ToggleButton::GetPreferredSize() const { |
| 167 gfx::Rect rect(0, 0, kTrackWidth, kTrackHeight); | 192 gfx::Rect rect(0, 0, kTrackWidth, kTrackHeight); |
| 193 rect.Inset(gfx::Insets(-kTrackVerticalMargin, -kTrackHorizontalMargin)); | |
| 168 if (border()) | 194 if (border()) |
| 169 rect.Inset(-border()->GetInsets()); | 195 rect.Inset(-border()->GetInsets()); |
| 170 return rect.size(); | 196 return rect.size(); |
| 171 } | 197 } |
| 172 | 198 |
| 173 const char* ToggleButton::GetClassName() const { | 199 const char* ToggleButton::GetClassName() const { |
| 174 return kViewClassName; | 200 return kViewClassName; |
| 175 } | 201 } |
| 176 | 202 |
| 177 void ToggleButton::OnPaint(gfx::Canvas* canvas) { | 203 void ToggleButton::OnPaint(gfx::Canvas* canvas) { |
| 178 // Paint the toggle track. To look sharp even at fractional scale factors, | 204 // Paint the toggle track. To look sharp even at fractional scale factors, |
| 179 // round up to pixel boundaries. | 205 // round up to pixel boundaries. |
| 180 float dsf = canvas->UndoDeviceScaleFactor(); | 206 { |
| 181 gfx::RectF track_rect(GetContentsBounds()); | 207 gfx::ScopedCanvas scoped_canvas(canvas); |
| 182 track_rect.Scale(dsf); | 208 float dsf = canvas->UndoDeviceScaleFactor(); |
| 183 track_rect = gfx::RectF(gfx::ToEnclosingRect(track_rect)); | 209 gfx::RectF track_rect((width() - kTrackWidth) / 2, |
| 184 SkPaint track_paint; | 210 (height() - kTrackHeight) / 2, kTrackWidth, |
| 185 track_paint.setAntiAlias(true); | 211 kTrackHeight); |
| 186 const double color_ratio = slide_animation_.GetCurrentValue(); | 212 track_rect.Scale(dsf); |
| 187 track_paint.setColor(color_utils::AlphaBlend( | 213 track_rect = gfx::RectF(gfx::ToEnclosingRect(track_rect)); |
| 188 GetTrackColor(true), GetTrackColor(false), | 214 SkPaint track_paint; |
| 189 static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio))); | 215 track_paint.setAntiAlias(true); |
| 190 canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint); | 216 const double color_ratio = slide_animation_.GetCurrentValue(); |
| 217 track_paint.setColor(color_utils::AlphaBlend( | |
| 218 GetTrackColor(true), GetTrackColor(false), | |
| 219 static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio))); | |
| 220 canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint); | |
| 221 } | |
| 222 views::Painter::PaintFocusPainter(this, canvas, focus_painter()); | |
| 191 } | 223 } |
| 192 | 224 |
| 193 void ToggleButton::NotifyClick(const ui::Event& event) { | 225 void ToggleButton::NotifyClick(const ui::Event& event) { |
| 194 SetIsOn(!is_on(), true); | 226 SetIsOn(!is_on(), true); |
| 195 CustomButton::NotifyClick(event); | 227 CustomButton::NotifyClick(event); |
| 196 } | 228 } |
| 197 | 229 |
| 198 void ToggleButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { | 230 void ToggleButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
| 199 UpdateThumb(); | 231 UpdateThumb(); |
| 200 } | 232 } |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 241 // TODO(varkha, estade): The thumb is using its own view. Investigate if | 273 // TODO(varkha, estade): The thumb is using its own view. Investigate if |
| 242 // repainting in every animation step to update colors could be avoided. | 274 // repainting in every animation step to update colors could be avoided. |
| 243 UpdateThumb(); | 275 UpdateThumb(); |
| 244 SchedulePaint(); | 276 SchedulePaint(); |
| 245 return; | 277 return; |
| 246 } | 278 } |
| 247 CustomButton::AnimationProgressed(animation); | 279 CustomButton::AnimationProgressed(animation); |
| 248 } | 280 } |
| 249 | 281 |
| 250 } // namespace views | 282 } // namespace views |
| OLD | NEW |