| 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 hovered, the thumb takes up the full width. Otherwise, it's a bit |
| 21 const int kThumbCornerRadius = 2; | 21 // slimmer. |
| 22 const int kThumbMinimumSize = kScrollbarWidth; | 22 const int kThumbUnhoveredDifference = 4; |
| 23 const int kThumbHoverAlpha = 128; | 23 const int kThumbStroke = 1; |
| 24 const int kThumbDefaultAlpha = 64; | 24 const float kThumbHoverAlpha = 0.5f; |
| 25 | 25 const float kThumbDefaultAlpha = 0.3f; |
| 26 class OverlayScrollBarThumb : public BaseScrollBarThumb, | |
| 27 public gfx::AnimationDelegate { | |
| 28 public: | |
| 29 explicit OverlayScrollBarThumb(BaseScrollBar* scroll_bar); | |
| 30 ~OverlayScrollBarThumb() override; | |
| 31 | |
| 32 protected: | |
| 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), | |
| 47 animation_opacity_(0.0) { | |
| 48 // This is necessary, otherwise the thumb will be rendered below the views if | |
| 49 // those views paint to their own layers. | |
| 50 SetPaintToLayer(true); | |
| 51 layer()->SetFillsBoundsOpaquely(false); | |
| 52 } | |
| 53 | |
| 54 OverlayScrollBarThumb::~OverlayScrollBarThumb() { | |
| 55 } | |
| 56 | |
| 57 gfx::Size OverlayScrollBarThumb::GetPreferredSize() const { | |
| 58 return gfx::Size(kThumbMinimumSize, kThumbMinimumSize); | |
| 59 } | |
| 60 | |
| 61 void OverlayScrollBarThumb::OnPaint(gfx::Canvas* canvas) { | |
| 62 gfx::Rect local_bounds(GetLocalBounds()); | |
| 63 SkPaint paint; | |
| 64 int alpha = kThumbDefaultAlpha * animation_opacity_; | |
| 65 if (GetState() == CustomButton::STATE_HOVERED) { | |
| 66 alpha = kThumbHoverAlpha * animation_opacity_; | |
| 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 } | |
| 77 | |
| 78 void OverlayScrollBarThumb::AnimationProgressed( | |
| 79 const gfx::Animation* animation) { | |
| 80 animation_opacity_ = animation->GetCurrentValue(); | |
| 81 SchedulePaint(); | |
| 82 } | |
| 83 | 26 |
| 84 } // namespace | 27 } // namespace |
| 85 | 28 |
| 29 OverlayScrollBar::Thumb::Thumb(OverlayScrollBar* scroll_bar) |
| 30 : BaseScrollBarThumb(scroll_bar), scroll_bar_(scroll_bar) { |
| 31 SetPaintToLayer(true); |
| 32 // Animate all changes to the layer except the first one. |
| 33 OnStateChanged(); |
| 34 layer()->SetAnimator(ui::LayerAnimator::CreateImplicitAnimator()); |
| 35 } |
| 36 |
| 37 OverlayScrollBar::Thumb::~Thumb() {} |
| 38 |
| 39 gfx::Size OverlayScrollBar::Thumb::GetPreferredSize() const { |
| 40 return gfx::Size(kThumbThickness, kThumbThickness); |
| 41 } |
| 42 |
| 43 void OverlayScrollBar::Thumb::OnPaint(gfx::Canvas* canvas) { |
| 44 SkPaint fill_paint; |
| 45 fill_paint.setStyle(SkPaint::kFill_Style); |
| 46 fill_paint.setColor(SK_ColorBLACK); |
| 47 gfx::RectF fill_bounds(GetLocalBounds()); |
| 48 fill_bounds.Inset(gfx::InsetsF(kThumbStroke, kThumbStroke, |
| 49 IsHorizontal() ? 0 : kThumbStroke, |
| 50 IsHorizontal() ? kThumbStroke : 0)); |
| 51 canvas->DrawRect(fill_bounds, fill_paint); |
| 52 |
| 53 SkPaint stroke_paint; |
| 54 stroke_paint.setStyle(SkPaint::kStroke_Style); |
| 55 stroke_paint.setColor(SK_ColorWHITE); |
| 56 stroke_paint.setStrokeWidth(kThumbStroke); |
| 57 gfx::RectF stroke_bounds(fill_bounds); |
| 58 stroke_bounds.Inset(gfx::InsetsF(-0.5f)); |
| 59 // The stroke doesn't apply to the far edge of the thumb. |
| 60 SkPath path; |
| 61 path.moveTo(gfx::PointFToSkPoint(stroke_bounds.top_right())); |
| 62 path.lineTo(gfx::PointFToSkPoint(stroke_bounds.origin())); |
| 63 path.lineTo(gfx::PointFToSkPoint(stroke_bounds.bottom_left())); |
| 64 if (IsHorizontal()) { |
| 65 path.moveTo(gfx::PointFToSkPoint(stroke_bounds.bottom_right())); |
| 66 path.close(); |
| 67 } else { |
| 68 path.lineTo(gfx::PointFToSkPoint(stroke_bounds.bottom_right())); |
| 69 } |
| 70 canvas->DrawPath(path, stroke_paint); |
| 71 } |
| 72 |
| 73 void OverlayScrollBar::Thumb::OnBoundsChanged( |
| 74 const gfx::Rect& previous_bounds) { |
| 75 scroll_bar_->Show(); |
| 76 // Don't start the hide countdown if the thumb is still hovered or pressed. |
| 77 if (GetState() == CustomButton::STATE_NORMAL) |
| 78 scroll_bar_->StartHideCountdown(); |
| 79 } |
| 80 |
| 81 void OverlayScrollBar::Thumb::OnStateChanged() { |
| 82 if (GetState() == CustomButton::STATE_NORMAL) { |
| 83 gfx::Transform translation; |
| 84 translation.Translate( |
| 85 gfx::Vector2d(IsHorizontal() ? 0 : kThumbUnhoveredDifference, |
| 86 IsHorizontal() ? kThumbUnhoveredDifference : 0)); |
| 87 layer()->SetTransform(translation); |
| 88 layer()->SetOpacity(kThumbDefaultAlpha); |
| 89 |
| 90 if (GetWidget()) |
| 91 scroll_bar_->StartHideCountdown(); |
| 92 } else { |
| 93 layer()->SetTransform(gfx::Transform()); |
| 94 layer()->SetOpacity(kThumbHoverAlpha); |
| 95 } |
| 96 } |
| 97 |
| 86 OverlayScrollBar::OverlayScrollBar(bool horizontal) | 98 OverlayScrollBar::OverlayScrollBar(bool horizontal) |
| 87 : BaseScrollBar(horizontal, new OverlayScrollBarThumb(this)), | 99 : BaseScrollBar(horizontal, new Thumb(this)), hide_timer_(false, false) { |
| 88 animation_(static_cast<OverlayScrollBarThumb*>(GetThumb())) { | |
| 89 set_notify_enter_exit_on_child(true); | 100 set_notify_enter_exit_on_child(true); |
| 101 SetPaintToLayer(true); |
| 102 layer()->SetMasksToBounds(true); |
| 103 layer()->SetFillsBoundsOpaquely(false); |
| 90 } | 104 } |
| 91 | 105 |
| 92 OverlayScrollBar::~OverlayScrollBar() { | 106 OverlayScrollBar::~OverlayScrollBar() { |
| 93 } | 107 } |
| 94 | 108 |
| 95 gfx::Rect OverlayScrollBar::GetTrackBounds() const { | 109 gfx::Rect OverlayScrollBar::GetTrackBounds() const { |
| 96 gfx::Rect local_bounds(GetLocalBounds()); | 110 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 } | 111 } |
| 109 | 112 |
| 110 int OverlayScrollBar::GetLayoutSize() const { | 113 int OverlayScrollBar::GetLayoutSize() const { |
| 111 return 0; | 114 return 0; |
| 112 } | 115 } |
| 113 | 116 |
| 114 int OverlayScrollBar::GetContentOverlapSize() const { | 117 int OverlayScrollBar::GetContentOverlapSize() const { |
| 115 return kScrollbarWidth; | 118 return kThumbThickness; |
| 116 } | 119 } |
| 117 | 120 |
| 118 void OverlayScrollBar::OnMouseEnteredScrollView(const ui::MouseEvent& event) { | 121 void OverlayScrollBar::OnMouseEntered(const ui::MouseEvent& event) { |
| 119 animation_.Show(); | 122 Show(); |
| 120 } | 123 } |
| 121 | 124 |
| 122 void OverlayScrollBar::OnMouseExitedScrollView(const ui::MouseEvent& event) { | 125 void OverlayScrollBar::OnMouseExited(const ui::MouseEvent& event) { |
| 123 animation_.Hide(); | 126 StartHideCountdown(); |
| 124 } | 127 } |
| 125 | 128 |
| 126 void OverlayScrollBar::OnGestureEvent(ui::GestureEvent* event) { | 129 void OverlayScrollBar::Show() { |
| 127 switch (event->type()) { | 130 layer()->SetOpacity(1.0f); |
| 128 case ui::ET_GESTURE_SCROLL_BEGIN: | 131 hide_timer_.Stop(); |
| 129 animation_.Show(); | 132 } |
| 130 break; | 133 |
| 131 case ui::ET_GESTURE_SCROLL_END: | 134 void OverlayScrollBar::Hide() { |
| 132 case ui::ET_SCROLL_FLING_START: | 135 ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator()); |
| 133 case ui::ET_GESTURE_END: | 136 settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(200)); |
| 134 animation_.Hide(); | 137 layer()->SetOpacity(0.0f); |
| 135 break; | 138 } |
| 136 default: | 139 |
| 137 break; | 140 void OverlayScrollBar::StartHideCountdown() { |
| 138 } | 141 if (IsMouseHovered()) |
| 139 BaseScrollBar::OnGestureEvent(event); | 142 return; |
| 143 hide_timer_.Start( |
| 144 FROM_HERE, base::TimeDelta::FromSeconds(1), |
| 145 base::Bind(&OverlayScrollBar::Hide, base::Unretained(this))); |
| 140 } | 146 } |
| 141 | 147 |
| 142 void OverlayScrollBar::Layout() { | 148 void OverlayScrollBar::Layout() { |
| 143 gfx::Rect thumb_bounds = GetTrackBounds(); | 149 gfx::Rect thumb_bounds = GetTrackBounds(); |
| 144 BaseScrollBarThumb* thumb = GetThumb(); | 150 BaseScrollBarThumb* thumb = GetThumb(); |
| 145 if (IsHorizontal()) { | 151 if (IsHorizontal()) { |
| 146 thumb_bounds.set_x(thumb->x()); | 152 thumb_bounds.set_x(thumb->x()); |
| 147 thumb_bounds.set_width(thumb->width()); | 153 thumb_bounds.set_width(thumb->width()); |
| 148 } else { | 154 } else { |
| 149 thumb_bounds.set_y(thumb->y()); | 155 thumb_bounds.set_y(thumb->y()); |
| 150 thumb_bounds.set_height(thumb->height()); | 156 thumb_bounds.set_height(thumb->height()); |
| 151 } | 157 } |
| 152 thumb->SetBoundsRect(thumb_bounds); | 158 thumb->SetBoundsRect(thumb_bounds); |
| 153 } | 159 } |
| 154 | 160 |
| 155 } // namespace views | 161 } // namespace views |
| OLD | NEW |