| 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/geometry/rect_conversions.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() : color_ratio_(0.) {} |
| 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 protected: |
| 54 // views::View: |
| 55 bool CanProcessEventsWithinSubtree() const override { |
| 56 // Make the thumb behave as part of the parent for event handling. |
| 57 return false; |
| 58 } |
| 59 |
| 54 private: | 60 private: |
| 55 static const int kShadowOffsetX = 0; | 61 static const int kShadowOffsetX = 0; |
| 56 static const int kShadowOffsetY = 1; | 62 static const int kShadowOffsetY = 1; |
| 57 static const int kShadowBlur = 2; | 63 static const int kShadowBlur = 2; |
| 58 | 64 |
| 59 // views::View: | 65 // views::View: |
| 60 const char* GetClassName() const override { | 66 const char* GetClassName() const override { |
| 61 return "ToggleButton::ThumbView"; | 67 return "ToggleButton::ThumbView"; |
| 62 } | 68 } |
| 63 | 69 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 101 // static | 107 // static |
| 102 const char ToggleButton::kViewClassName[] = "ToggleButton"; | 108 const char ToggleButton::kViewClassName[] = "ToggleButton"; |
| 103 | 109 |
| 104 ToggleButton::ToggleButton(ButtonListener* listener) | 110 ToggleButton::ToggleButton(ButtonListener* listener) |
| 105 : CustomButton(listener), | 111 : CustomButton(listener), |
| 106 is_on_(false), | 112 is_on_(false), |
| 107 slide_animation_(this), | 113 slide_animation_(this), |
| 108 thumb_view_(new ThumbView()) { | 114 thumb_view_(new ThumbView()) { |
| 109 slide_animation_.SetSlideDuration(80 /* ms */); | 115 slide_animation_.SetSlideDuration(80 /* ms */); |
| 110 slide_animation_.SetTweenType(gfx::Tween::LINEAR); | 116 slide_animation_.SetTweenType(gfx::Tween::LINEAR); |
| 111 SetBorder(CreateEmptyBorder( | |
| 112 gfx::Insets(kTrackVerticalMargin, kTrackHorizontalMargin))); | |
| 113 AddChildView(thumb_view_); | 117 AddChildView(thumb_view_); |
| 114 SetInkDropMode(InkDropMode::ON); | 118 SetInkDropMode(InkDropMode::ON); |
| 119 SetFocusForPlatform(); |
| 115 set_has_ink_drop_action_on_click(true); | 120 set_has_ink_drop_action_on_click(true); |
| 116 } | 121 } |
| 117 | 122 |
| 118 ToggleButton::~ToggleButton() { | 123 ToggleButton::~ToggleButton() { |
| 119 // Destroying ink drop early allows ink drop layer to be properly removed, | 124 // Destroying ink drop early allows ink drop layer to be properly removed, |
| 120 SetInkDropMode(InkDropMode::OFF); | 125 SetInkDropMode(InkDropMode::OFF); |
| 121 } | 126 } |
| 122 | 127 |
| 123 void ToggleButton::SetIsOn(bool is_on, bool animate) { | 128 void ToggleButton::SetIsOn(bool is_on, bool animate) { |
| 124 if (is_on_ == is_on) | 129 if (is_on_ == is_on) |
| 125 return; | 130 return; |
| 126 | 131 |
| 127 is_on_ = is_on; | 132 is_on_ = is_on; |
| 128 if (!animate) { | 133 if (!animate) { |
| 129 slide_animation_.Reset(is_on_ ? 1.0 : 0.0); | 134 slide_animation_.Reset(is_on_ ? 1.0 : 0.0); |
| 130 UpdateThumb(); | 135 UpdateThumb(); |
| 131 SchedulePaint(); | 136 SchedulePaint(); |
| 132 } else if (is_on_) { | 137 } else if (is_on_) { |
| 133 slide_animation_.Show(); | 138 slide_animation_.Show(); |
| 134 } else { | 139 } else { |
| 135 slide_animation_.Hide(); | 140 slide_animation_.Hide(); |
| 136 } | 141 } |
| 137 } | 142 } |
| 138 | 143 |
| 144 void ToggleButton::SetFocusPainter(std::unique_ptr<Painter> focus_painter) { |
| 145 focus_painter_ = std::move(focus_painter); |
| 146 } |
| 147 |
| 148 gfx::Size ToggleButton::GetPreferredSize() const { |
| 149 gfx::Rect rect(kTrackWidth, kTrackHeight); |
| 150 rect.Inset(gfx::Insets(-kTrackVerticalMargin, -kTrackHorizontalMargin)); |
| 151 if (border()) |
| 152 rect.Inset(-border()->GetInsets()); |
| 153 return rect.size(); |
| 154 } |
| 155 |
| 156 gfx::Rect ToggleButton::GetTrackBounds() const { |
| 157 gfx::Rect track_bounds(GetContentsBounds()); |
| 158 track_bounds.ClampToCenteredSize(gfx::Size(kTrackWidth, kTrackHeight)); |
| 159 return track_bounds; |
| 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(GetTrackBounds()); |
| 141 thumb_bounds.Inset(gfx::Insets(kThumbVerticalMargin, kThumbHorizontalMargin)); | 164 thumb_bounds.Inset(gfx::Insets(-kThumbInset)); |
| 142 thumb_bounds.set_x(thumb_bounds.x() + | 165 thumb_bounds.set_x(thumb_bounds.x() + |
| 143 slide_animation_.GetCurrentValue() * | 166 slide_animation_.GetCurrentValue() * |
| 144 (thumb_bounds.width() - thumb_bounds.height())); | 167 (thumb_bounds.width() - thumb_bounds.height())); |
| 145 // The thumb is a circle, so the width should match the height. | 168 // The thumb is a circle, so the width should match the height. |
| 146 thumb_bounds.set_width(thumb_bounds.height()); | 169 thumb_bounds.set_width(thumb_bounds.height()); |
| 147 thumb_bounds.set_x(GetMirroredXForRect(thumb_bounds)); | 170 thumb_bounds.set_x(GetMirroredXForRect(thumb_bounds)); |
| 148 thumb_bounds.Inset(ThumbView::GetShadowOutsets()); | 171 thumb_bounds.Inset(ThumbView::GetShadowOutsets()); |
| 149 return thumb_bounds; | 172 return thumb_bounds; |
| 150 } | 173 } |
| 151 | 174 |
| 152 void ToggleButton::UpdateThumb() { | 175 void ToggleButton::UpdateThumb() { |
| 153 thumb_view_->Update(GetThumbBounds(), slide_animation_.GetCurrentValue()); | 176 thumb_view_->Update(GetThumbBounds(), slide_animation_.GetCurrentValue()); |
| 154 } | 177 } |
| 155 | 178 |
| 156 SkColor ToggleButton::GetTrackColor(bool is_on) const { | 179 SkColor ToggleButton::GetTrackColor(bool is_on) const { |
| 157 const SkAlpha kTrackAlpha = 0x66; | 180 const SkAlpha kTrackAlpha = 0x66; |
| 158 ui::NativeTheme::ColorId color_id = | 181 ui::NativeTheme::ColorId color_id = |
| 159 is_on ? ui::NativeTheme::kColorId_ProminentButtonColor | 182 is_on ? ui::NativeTheme::kColorId_ProminentButtonColor |
| 160 : ui::NativeTheme::kColorId_LabelEnabledColor; | 183 : ui::NativeTheme::kColorId_LabelEnabledColor; |
| 161 return SkColorSetA(GetNativeTheme()->GetSystemColor(color_id), kTrackAlpha); | 184 return SkColorSetA(GetNativeTheme()->GetSystemColor(color_id), kTrackAlpha); |
| 162 } | 185 } |
| 163 | 186 |
| 164 gfx::Size ToggleButton::GetPreferredSize() const { | |
| 165 gfx::Rect rect(0, 0, kTrackWidth, kTrackHeight); | |
| 166 if (border()) | |
| 167 rect.Inset(-border()->GetInsets()); | |
| 168 return rect.size(); | |
| 169 } | |
| 170 | |
| 171 const char* ToggleButton::GetClassName() const { | 187 const char* ToggleButton::GetClassName() const { |
| 172 return kViewClassName; | 188 return kViewClassName; |
| 173 } | 189 } |
| 174 | 190 |
| 175 void ToggleButton::OnPaint(gfx::Canvas* canvas) { | 191 void ToggleButton::OnPaint(gfx::Canvas* canvas) { |
| 176 // Paint the toggle track. To look sharp even at fractional scale factors, | 192 // Paint the toggle track. To look sharp even at fractional scale factors, |
| 177 // round up to pixel boundaries. | 193 // round up to pixel boundaries. |
| 194 canvas->Save(); |
| 178 float dsf = canvas->UndoDeviceScaleFactor(); | 195 float dsf = canvas->UndoDeviceScaleFactor(); |
| 179 gfx::RectF track_rect(GetContentsBounds()); | 196 gfx::RectF track_rect(GetTrackBounds()); |
| 180 track_rect.Scale(dsf); | 197 track_rect.Scale(dsf); |
| 181 track_rect = gfx::RectF(gfx::ToEnclosingRect(track_rect)); | 198 track_rect = gfx::RectF(gfx::ToEnclosingRect(track_rect)); |
| 182 SkPaint track_paint; | 199 SkPaint track_paint; |
| 183 track_paint.setAntiAlias(true); | 200 track_paint.setAntiAlias(true); |
| 184 const double color_ratio = slide_animation_.GetCurrentValue(); | 201 const double color_ratio = slide_animation_.GetCurrentValue(); |
| 185 track_paint.setColor(color_utils::AlphaBlend( | 202 track_paint.setColor(color_utils::AlphaBlend( |
| 186 GetTrackColor(true), GetTrackColor(false), | 203 GetTrackColor(true), GetTrackColor(false), |
| 187 static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio))); | 204 static_cast<SkAlpha>(SK_AlphaOPAQUE * color_ratio))); |
| 188 canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint); | 205 canvas->DrawRoundRect(track_rect, track_rect.height() / 2, track_paint); |
| 206 canvas->Restore(); |
| 207 |
| 208 Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); |
| 189 } | 209 } |
| 190 | 210 |
| 191 void ToggleButton::NotifyClick(const ui::Event& event) { | 211 void ToggleButton::OnFocus() { |
| 192 SetIsOn(!is_on(), true); | 212 CustomButton::OnFocus(); |
| 193 CustomButton::NotifyClick(event); | 213 if (focus_painter_) |
| 214 SchedulePaint(); |
| 215 } |
| 216 |
| 217 void ToggleButton::OnBlur() { |
| 218 CustomButton::OnBlur(); |
| 219 if (focus_painter_) |
| 220 SchedulePaint(); |
| 194 } | 221 } |
| 195 | 222 |
| 196 void ToggleButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { | 223 void ToggleButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
| 197 UpdateThumb(); | 224 UpdateThumb(); |
| 198 } | 225 } |
| 199 | 226 |
| 200 void ToggleButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { | 227 void ToggleButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
| 201 SchedulePaint(); | 228 SchedulePaint(); |
| 202 } | 229 } |
| 203 | 230 |
| 204 void ToggleButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { | 231 void ToggleButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { |
| 205 CustomButton::GetAccessibleNodeData(node_data); | 232 CustomButton::GetAccessibleNodeData(node_data); |
| 206 | 233 |
| 207 node_data->role = ui::AX_ROLE_SWITCH; | 234 node_data->role = ui::AX_ROLE_SWITCH; |
| 208 if (is_on_) | 235 if (is_on_) |
| 209 node_data->AddStateFlag(ui::AX_STATE_CHECKED); | 236 node_data->AddStateFlag(ui::AX_STATE_CHECKED); |
| 210 } | 237 } |
| 211 | 238 |
| 239 void ToggleButton::NotifyClick(const ui::Event& event) { |
| 240 SetIsOn(!is_on(), true); |
| 241 CustomButton::NotifyClick(event); |
| 242 } |
| 243 |
| 212 void ToggleButton::AddInkDropLayer(ui::Layer* ink_drop_layer) { | 244 void ToggleButton::AddInkDropLayer(ui::Layer* ink_drop_layer) { |
| 213 thumb_view_->AddInkDropLayer(ink_drop_layer); | 245 thumb_view_->AddInkDropLayer(ink_drop_layer); |
| 214 } | 246 } |
| 215 | 247 |
| 216 void ToggleButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { | 248 void ToggleButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { |
| 217 thumb_view_->RemoveInkDropLayer(ink_drop_layer); | 249 thumb_view_->RemoveInkDropLayer(ink_drop_layer); |
| 218 } | 250 } |
| 219 | 251 |
| 220 std::unique_ptr<InkDrop> ToggleButton::CreateInkDrop() { | 252 std::unique_ptr<InkDrop> ToggleButton::CreateInkDrop() { |
| 221 std::unique_ptr<InkDropImpl> ink_drop = | 253 std::unique_ptr<InkDropImpl> ink_drop = |
| (...skipping 17 matching lines...) Expand all Loading... |
| 239 // TODO(varkha, estade): The thumb is using its own view. Investigate if | 271 // TODO(varkha, estade): The thumb is using its own view. Investigate if |
| 240 // repainting in every animation step to update colors could be avoided. | 272 // repainting in every animation step to update colors could be avoided. |
| 241 UpdateThumb(); | 273 UpdateThumb(); |
| 242 SchedulePaint(); | 274 SchedulePaint(); |
| 243 return; | 275 return; |
| 244 } | 276 } |
| 245 CustomButton::AnimationProgressed(animation); | 277 CustomButton::AnimationProgressed(animation); |
| 246 } | 278 } |
| 247 | 279 |
| 248 } // namespace views | 280 } // namespace views |
| OLD | NEW |