Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/scrollbar/overlay_scroll_bar.h" | 5 #include "ui/views/controls/scrollbar/overlay_scroll_bar.h" |
| 6 | 6 |
| 7 #include "base/macros.h" | 7 #include "base/macros.h" |
| 8 #include "third_party/skia/include/core/SkColor.h" | 8 #include "third_party/skia/include/core/SkColor.h" |
| 9 #include "third_party/skia/include/core/SkXfermode.h" | 9 #include "third_party/skia/include/core/SkXfermode.h" |
| 10 #include "ui/compositor/scoped_layer_animation_settings.h" | |
| 10 #include "ui/gfx/canvas.h" | 11 #include "ui/gfx/canvas.h" |
| 11 #include "ui/views/background.h" | 12 #include "ui/views/background.h" |
| 12 #include "ui/views/border.h" | 13 #include "ui/views/border.h" |
| 13 #include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h" | |
| 14 | 14 |
| 15 namespace views { | 15 namespace views { |
| 16 namespace { | 16 namespace { |
| 17 | 17 |
| 18 const int kScrollbarWidth = 10; | 18 // Total thickness of the thumb (matches visuals when hovered). |
| 19 const int kThumbInsetInside = 3; | 19 const int kThumbThickness = 11; |
| 20 const int kThumbInsetFromEdge = 1; | 20 // When unhovered, the thumb is slimmer by this amount of dip. |
| 21 const int kThumbCornerRadius = 2; | 21 const int kThumbUnhoveredDifference = 4; |
| 22 const int kThumbMinimumSize = kScrollbarWidth; | 22 const int kThumbStroke = 1; |
| 23 const int kThumbHoverAlpha = 128; | 23 const SkAlpha kThumbHoverAlpha = 0x80; // 0.5 |
| 24 const int kThumbDefaultAlpha = 64; | 24 const SkAlpha kThumbDefaultAlpha = 0x4D; // 0.3 |
| 25 | 25 |
| 26 class OverlayScrollBarThumb : public BaseScrollBarThumb, | 26 } // namespace |
| 27 public gfx::AnimationDelegate { | |
| 28 public: | |
| 29 explicit OverlayScrollBarThumb(BaseScrollBar* scroll_bar); | |
| 30 ~OverlayScrollBarThumb() override; | |
| 31 | 27 |
| 32 protected: | 28 OverlayScrollBar::Thumb::Thumb(OverlayScrollBar* scroll_bar) |
| 33 // View overrides: | |
| 34 gfx::Size GetPreferredSize() const override; | |
| 35 void OnPaint(gfx::Canvas* canvas) override; | |
| 36 | |
| 37 // gfx::AnimationDelegate overrides: | |
| 38 void AnimationProgressed(const gfx::Animation* animation) override; | |
| 39 | |
| 40 private: | |
| 41 double animation_opacity_; | |
| 42 DISALLOW_COPY_AND_ASSIGN(OverlayScrollBarThumb); | |
| 43 }; | |
| 44 | |
| 45 OverlayScrollBarThumb::OverlayScrollBarThumb(BaseScrollBar* scroll_bar) | |
| 46 : BaseScrollBarThumb(scroll_bar), | 29 : BaseScrollBarThumb(scroll_bar), |
| 47 animation_opacity_(0.0) { | 30 scroll_bar_(scroll_bar), |
| 31 hover_animation_(this) { | |
| 48 // This is necessary, otherwise the thumb will be rendered below the views if | 32 // This is necessary, otherwise the thumb will be rendered below the views if |
| 49 // those views paint to their own layers. | 33 // those views paint to their own layers. |
| 50 SetPaintToLayer(true); | 34 SetPaintToLayer(true); |
| 51 layer()->SetFillsBoundsOpaquely(false); | 35 layer()->SetFillsBoundsOpaquely(false); |
| 36 layer()->SetOpacity(0); | |
| 52 } | 37 } |
| 53 | 38 |
| 54 OverlayScrollBarThumb::~OverlayScrollBarThumb() { | 39 OverlayScrollBar::Thumb::~Thumb() {} |
| 40 | |
| 41 gfx::Size OverlayScrollBar::Thumb::GetPreferredSize() const { | |
| 42 return gfx::Size(kThumbThickness, kThumbThickness); | |
| 55 } | 43 } |
| 56 | 44 |
| 57 gfx::Size OverlayScrollBarThumb::GetPreferredSize() const { | 45 void OverlayScrollBar::Thumb::OnPaint(gfx::Canvas* canvas) { |
| 58 return gfx::Size(kThumbMinimumSize, kThumbMinimumSize); | 46 SkAlpha alpha = kThumbHoverAlpha; |
| 47 // If we are in pressed state, no need to worry about animation, | |
| 48 // just display the deeper color. | |
| 49 if (GetState() != CustomButton::STATE_PRESSED) { | |
| 50 alpha = hover_animation_.CurrentValueBetween(kThumbDefaultAlpha, | |
| 51 kThumbHoverAlpha); | |
| 52 } | |
| 53 | |
| 54 SkPaint fill_paint; | |
| 55 fill_paint.setAntiAlias(true); | |
| 56 fill_paint.setStyle(SkPaint::kFill_Style); | |
| 57 fill_paint.setColor(SkColorSetA(SK_ColorBLACK, alpha)); | |
| 58 gfx::RectF fill_bounds(GetLocalBounds()); | |
| 59 const double slimmer_by = | |
| 60 hover_animation_.CurrentValueBetween(kThumbUnhoveredDifference, 0); | |
| 61 fill_bounds.Inset(gfx::InsetsF(IsHorizontal() ? slimmer_by : 0, | |
| 62 IsHorizontal() ? 0 : slimmer_by, 0, 0)); | |
| 63 fill_bounds.Inset(gfx::InsetsF(kThumbStroke, kThumbStroke, | |
| 64 IsHorizontal() ? 0 : kThumbStroke, | |
| 65 IsHorizontal() ? kThumbStroke : 0)); | |
| 66 canvas->DrawRect(fill_bounds, fill_paint); | |
| 67 | |
| 68 SkPaint stroke_paint; | |
| 69 stroke_paint.setAntiAlias(true); | |
| 70 stroke_paint.setStyle(SkPaint::kFill_Style); | |
| 71 stroke_paint.setColor(SkColorSetA(SK_ColorWHITE, alpha)); | |
| 72 stroke_paint.setStrokeWidth(kThumbStroke); | |
| 73 gfx::RectF stroke_bounds(fill_bounds); | |
| 74 stroke_bounds.Inset(gfx::InsetsF(-0.5f)); | |
| 75 // The stroke doesn't apply to the far edge of the thumb. | |
| 76 SkPath path; | |
| 77 path.moveTo(gfx::PointFToSkPoint(stroke_bounds.top_right())); | |
| 78 path.lineTo(gfx::PointFToSkPoint(stroke_bounds.origin())); | |
| 79 path.lineTo(gfx::PointFToSkPoint(stroke_bounds.bottom_left())); | |
| 80 if (IsHorizontal()) { | |
| 81 path.moveTo(gfx::PointFToSkPoint(stroke_bounds.bottom_right())); | |
| 82 path.close(); | |
| 83 } else { | |
| 84 path.lineTo(gfx::PointFToSkPoint(stroke_bounds.bottom_right())); | |
| 85 } | |
| 86 canvas->DrawPath(path, stroke_paint); | |
| 59 } | 87 } |
| 60 | 88 |
| 61 void OverlayScrollBarThumb::OnPaint(gfx::Canvas* canvas) { | 89 void OverlayScrollBar::Thumb::OnBoundsChanged( |
| 62 gfx::Rect local_bounds(GetLocalBounds()); | 90 const gfx::Rect& previous_bounds) { |
| 63 SkPaint paint; | 91 scroll_bar_->ShowThumb(); |
| 64 int alpha = kThumbDefaultAlpha * animation_opacity_; | 92 // Don't start the hide countdown if the thumb is still hovered or pressed. |
| 65 if (GetState() == CustomButton::STATE_HOVERED) { | 93 if (GetState() == CustomButton::STATE_NORMAL) |
| 66 alpha = kThumbHoverAlpha * animation_opacity_; | 94 scroll_bar_->StartThumbHideCountdown(); |
| 67 } else if(GetState() == CustomButton::STATE_PRESSED) { | |
| 68 // If we are in pressed state, no need to worry about animation, | |
| 69 // just display the deeper color. | |
| 70 alpha = kThumbHoverAlpha; | |
| 71 } | |
| 72 | |
| 73 paint.setStyle(SkPaint::kFill_Style); | |
| 74 paint.setColor(SkColorSetARGB(alpha, 0, 0, 0)); | |
| 75 canvas->DrawRoundRect(local_bounds, kThumbCornerRadius, paint); | |
| 76 } | 95 } |
| 77 | 96 |
| 78 void OverlayScrollBarThumb::AnimationProgressed( | 97 void OverlayScrollBar::Thumb::SetState(CustomButton::ButtonState state) { |
| 98 if (state != GetState()) { | |
| 99 if (state == CustomButton::STATE_NORMAL) { | |
| 100 hover_animation_.Hide(); | |
| 101 scroll_bar_->StartThumbHideCountdown(); | |
| 102 } else { | |
| 103 hover_animation_.Show(); | |
|
sadrul
2016/11/12 03:01:10
Since the thumb has a layer, can you just animate
Evan Stade
2016/11/14 16:56:07
I've changed all animations to be applied to layer
| |
| 104 } | |
| 105 } | |
| 106 BaseScrollBarThumb::SetState(state); | |
| 107 } | |
| 108 | |
| 109 void OverlayScrollBar::Thumb::AnimationProgressed( | |
| 79 const gfx::Animation* animation) { | 110 const gfx::Animation* animation) { |
| 80 animation_opacity_ = animation->GetCurrentValue(); | |
| 81 SchedulePaint(); | 111 SchedulePaint(); |
| 82 } | 112 } |
| 83 | 113 |
| 84 } // namespace | |
| 85 | |
| 86 OverlayScrollBar::OverlayScrollBar(bool horizontal) | 114 OverlayScrollBar::OverlayScrollBar(bool horizontal) |
| 87 : BaseScrollBar(horizontal, new OverlayScrollBarThumb(this)), | 115 : BaseScrollBar(horizontal, new Thumb(this)), |
| 88 animation_(static_cast<OverlayScrollBarThumb*>(GetThumb())) { | 116 thumb_hide_timer_(false, false) { |
| 89 set_notify_enter_exit_on_child(true); | 117 set_notify_enter_exit_on_child(true); |
| 90 } | 118 } |
| 91 | 119 |
| 92 OverlayScrollBar::~OverlayScrollBar() { | 120 OverlayScrollBar::~OverlayScrollBar() { |
| 93 } | 121 } |
| 94 | 122 |
| 95 gfx::Rect OverlayScrollBar::GetTrackBounds() const { | 123 gfx::Rect OverlayScrollBar::GetTrackBounds() const { |
| 96 gfx::Rect local_bounds(GetLocalBounds()); | 124 return GetLocalBounds(); |
| 97 if (IsHorizontal()) { | |
| 98 local_bounds.Inset(kThumbInsetFromEdge, kThumbInsetInside, | |
| 99 kThumbInsetFromEdge, kThumbInsetFromEdge); | |
| 100 } else { | |
| 101 local_bounds.Inset(kThumbInsetInside, kThumbInsetFromEdge, | |
| 102 kThumbInsetFromEdge, kThumbInsetFromEdge); | |
| 103 } | |
| 104 gfx::Size track_size = local_bounds.size(); | |
| 105 track_size.SetToMax(GetThumb()->size()); | |
| 106 local_bounds.set_size(track_size); | |
| 107 return local_bounds; | |
| 108 } | 125 } |
| 109 | 126 |
| 110 int OverlayScrollBar::GetLayoutSize() const { | 127 int OverlayScrollBar::GetLayoutSize() const { |
| 111 return 0; | 128 return 0; |
| 112 } | 129 } |
| 113 | 130 |
| 114 int OverlayScrollBar::GetContentOverlapSize() const { | 131 int OverlayScrollBar::GetContentOverlapSize() const { |
| 115 return kScrollbarWidth; | 132 return kThumbThickness; |
| 116 } | 133 } |
| 117 | 134 |
| 118 void OverlayScrollBar::OnMouseEnteredScrollView(const ui::MouseEvent& event) { | 135 void OverlayScrollBar::OnMouseEntered(const ui::MouseEvent& event) { |
| 119 animation_.Show(); | 136 ShowThumb(); |
| 120 } | 137 } |
| 121 | 138 |
| 122 void OverlayScrollBar::OnMouseExitedScrollView(const ui::MouseEvent& event) { | 139 void OverlayScrollBar::OnMouseExited(const ui::MouseEvent& event) { |
| 123 animation_.Hide(); | 140 StartThumbHideCountdown(); |
| 124 } | 141 } |
| 125 | 142 |
| 126 void OverlayScrollBar::OnGestureEvent(ui::GestureEvent* event) { | 143 void OverlayScrollBar::ShowThumb() { |
| 127 switch (event->type()) { | 144 GetThumb()->layer()->SetOpacity(1.0f); |
| 128 case ui::ET_GESTURE_SCROLL_BEGIN: | 145 thumb_hide_timer_.Stop(); |
| 129 animation_.Show(); | 146 } |
| 130 break; | 147 |
| 131 case ui::ET_GESTURE_SCROLL_END: | 148 void OverlayScrollBar::HideThumb() { |
| 132 case ui::ET_SCROLL_FLING_START: | 149 ui::ScopedLayerAnimationSettings settings(GetThumb()->layer()->GetAnimator()); |
| 133 case ui::ET_GESTURE_END: | 150 settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(200)); |
| 134 animation_.Hide(); | 151 GetThumb()->layer()->SetOpacity(0.0f); |
| 135 break; | 152 } |
| 136 default: | 153 |
| 137 break; | 154 void OverlayScrollBar::StartThumbHideCountdown() { |
| 138 } | 155 if (IsMouseHovered()) |
| 139 BaseScrollBar::OnGestureEvent(event); | 156 return; |
| 157 thumb_hide_timer_.Start( | |
| 158 FROM_HERE, base::TimeDelta::FromSeconds(1), | |
| 159 base::Bind(&OverlayScrollBar::HideThumb, base::Unretained(this))); | |
| 140 } | 160 } |
| 141 | 161 |
| 142 void OverlayScrollBar::Layout() { | 162 void OverlayScrollBar::Layout() { |
| 143 gfx::Rect thumb_bounds = GetTrackBounds(); | 163 gfx::Rect thumb_bounds = GetTrackBounds(); |
| 144 BaseScrollBarThumb* thumb = GetThumb(); | 164 BaseScrollBarThumb* thumb = GetThumb(); |
| 145 if (IsHorizontal()) { | 165 if (IsHorizontal()) { |
| 146 thumb_bounds.set_x(thumb->x()); | 166 thumb_bounds.set_x(thumb->x()); |
| 147 thumb_bounds.set_width(thumb->width()); | 167 thumb_bounds.set_width(thumb->width()); |
| 148 } else { | 168 } else { |
| 149 thumb_bounds.set_y(thumb->y()); | 169 thumb_bounds.set_y(thumb->y()); |
| 150 thumb_bounds.set_height(thumb->height()); | 170 thumb_bounds.set_height(thumb->height()); |
| 151 } | 171 } |
| 152 thumb->SetBoundsRect(thumb_bounds); | 172 thumb->SetBoundsRect(thumb_bounds); |
| 153 } | 173 } |
| 154 | 174 |
| 155 } // namespace views | 175 } // namespace views |
| OLD | NEW |