| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ui/views/controls/scrollbar/cocoa_scroll_bar.h" |
| 6 |
| 7 #import "base/mac/sdk_forward_declarations.h" |
| 8 #include "third_party/skia/include/core/SkColor.h" |
| 9 #include "third_party/skia/include/effects/SkGradientShader.h" |
| 10 #include "ui/compositor/layer.h" |
| 11 #include "ui/compositor/layer_animator.h" |
| 12 #include "ui/compositor/scoped_layer_animation_settings.h" |
| 13 #include "ui/gfx/canvas.h" |
| 14 #include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h" |
| 15 |
| 16 namespace views { |
| 17 |
| 18 namespace { |
| 19 |
| 20 // The length of the fade animation. |
| 21 const int kFadeDurationMs = 240; |
| 22 |
| 23 // How long we should wait before hiding the scrollbar. |
| 24 const int kScrollbarHideTimeoutMs = 500; |
| 25 |
| 26 // The width of the scrollbar. |
| 27 const int kScrollbarWidth = 15; |
| 28 |
| 29 // The width of the scrollbar thumb. |
| 30 const int kScrollbarThumbWidth = 10; |
| 31 |
| 32 // The width of the scroller track border. |
| 33 const int kScrollerTrackBorderWidth = 1; |
| 34 |
| 35 // The amount the thumb is inset from both the ends and the sides of the track. |
| 36 const int kScrollbarThumbInset = 3; |
| 37 |
| 38 // Scrollbar thumb colors. |
| 39 const SkColor kScrollerDefaultThumbColor = SkColorSetARGB(0x38, 0, 0, 0); |
| 40 const SkColor kScrollerHoverThumbColor = SkColorSetARGB(0x80, 0, 0, 0); |
| 41 |
| 42 // Scroller track colors. |
| 43 const SkColor kScrollerTrackGradientColors[] = { |
| 44 SkColorSetRGB(0xEF, 0xEF, 0xEF), SkColorSetRGB(0xF9, 0xF9, 0xF9), |
| 45 SkColorSetRGB(0xFD, 0xFD, 0xFD), SkColorSetRGB(0xF6, 0xF6, 0xF6)}; |
| 46 const SkColor kScrollerTrackInnerBorderColor = SkColorSetRGB(0xE4, 0xE4, 0xE4); |
| 47 const SkColor kScrollerTrackOuterBorderColor = SkColorSetRGB(0xEF, 0xEF, 0xEF); |
| 48 |
| 49 ////////////////////////////////////////////////////////////////// |
| 50 // CocoaScrollBarThumb |
| 51 |
| 52 class CocoaScrollBarThumb : public BaseScrollBarThumb { |
| 53 public: |
| 54 explicit CocoaScrollBarThumb(CocoaScrollBar* scroll_bar); |
| 55 ~CocoaScrollBarThumb() override; |
| 56 |
| 57 // Returns true if the thumb is in hover or pressed state. |
| 58 bool IsStateHoverOrPressed(); |
| 59 |
| 60 protected: |
| 61 // View: |
| 62 gfx::Size GetPreferredSize() const override; |
| 63 void OnPaint(gfx::Canvas* canvas) override; |
| 64 void OnMouseEntered(const ui::MouseEvent& event) override; |
| 65 |
| 66 private: |
| 67 DISALLOW_COPY_AND_ASSIGN(CocoaScrollBarThumb); |
| 68 }; |
| 69 |
| 70 CocoaScrollBarThumb::CocoaScrollBarThumb(CocoaScrollBar* scroll_bar) |
| 71 : BaseScrollBarThumb(scroll_bar) { |
| 72 DCHECK(scroll_bar); |
| 73 |
| 74 // This is necessary, otherwise the thumb will be rendered below the views if |
| 75 // those views paint to their own layers. |
| 76 SetPaintToLayer(true); |
| 77 SetFillsBoundsOpaquely(false); |
| 78 } |
| 79 |
| 80 CocoaScrollBarThumb::~CocoaScrollBarThumb() {} |
| 81 |
| 82 bool CocoaScrollBarThumb::IsStateHoverOrPressed() { |
| 83 CustomButton::ButtonState state = GetState(); |
| 84 return state == CustomButton::STATE_HOVERED || |
| 85 state == CustomButton::STATE_PRESSED; |
| 86 } |
| 87 |
| 88 gfx::Size CocoaScrollBarThumb::GetPreferredSize() const { |
| 89 return gfx::Size(kScrollbarThumbWidth, kScrollbarThumbWidth); |
| 90 } |
| 91 |
| 92 void CocoaScrollBarThumb::OnPaint(gfx::Canvas* canvas) { |
| 93 CocoaScrollBar* scrollbar = static_cast<CocoaScrollBar*>(scroll_bar()); |
| 94 DCHECK(scrollbar); |
| 95 |
| 96 SkColor thumb_color = kScrollerDefaultThumbColor; |
| 97 if (scrollbar->GetScrollerStyle() == NSScrollerStyleOverlay || |
| 98 IsStateHoverOrPressed()) |
| 99 thumb_color = kScrollerHoverThumbColor; |
| 100 |
| 101 gfx::Rect local_bounds(GetLocalBounds()); |
| 102 SkPaint paint; |
| 103 paint.setAntiAlias(true); |
| 104 paint.setStyle(SkPaint::kFill_Style); |
| 105 paint.setColor(thumb_color); |
| 106 const SkScalar radius = std::min(local_bounds.width(), local_bounds.height()); |
| 107 canvas->DrawRoundRect(local_bounds, radius, paint); |
| 108 } |
| 109 |
| 110 void CocoaScrollBarThumb::OnMouseEntered(const ui::MouseEvent& event) { |
| 111 BaseScrollBarThumb::OnMouseEntered(event); |
| 112 CocoaScrollBar* scrollbar = static_cast<CocoaScrollBar*>(scroll_bar()); |
| 113 scrollbar->OnMouseEnteredScrollbarThumb(event); |
| 114 } |
| 115 |
| 116 } // namespace |
| 117 |
| 118 ////////////////////////////////////////////////////////////////// |
| 119 // CocoaScrollBar class |
| 120 |
| 121 CocoaScrollBar::CocoaScrollBar(bool horizontal) |
| 122 : BaseScrollBar(horizontal, new CocoaScrollBarThumb(this)), |
| 123 hide_scrollbar_timer_( |
| 124 FROM_HERE, |
| 125 base::TimeDelta::FromMilliseconds(kScrollbarHideTimeoutMs), |
| 126 base::Bind(&CocoaScrollBar::HideScrollbar, base::Unretained(this)), |
| 127 false) { |
| 128 bridge_.reset([[ViewsScrollbarBridge alloc] init]); |
| 129 [bridge_ setDelegate:this]; |
| 130 |
| 131 scroller_style_ = [ViewsScrollbarBridge getPreferredScrollerStyle]; |
| 132 |
| 133 SetPaintToLayer(true); |
| 134 has_scrolltrack_ = scroller_style_ == NSScrollerStyleLegacy; |
| 135 layer()->SetOpacity(scroller_style_ == NSScrollerStyleOverlay ? 0.0 : 1.0); |
| 136 } |
| 137 |
| 138 CocoaScrollBar::~CocoaScrollBar() { |
| 139 [bridge_ setDelegate:nullptr]; |
| 140 } |
| 141 |
| 142 ////////////////////////////////////////////////////////////////// |
| 143 // CocoaScrollBar, BaseScrollBar: |
| 144 |
| 145 gfx::Rect CocoaScrollBar::GetTrackBounds() const { |
| 146 gfx::Rect local_bounds(GetLocalBounds()); |
| 147 local_bounds.Inset(kScrollbarThumbInset, kScrollbarThumbInset); |
| 148 |
| 149 gfx::Size track_size = local_bounds.size(); |
| 150 track_size.SetToMax(GetThumb()->size()); |
| 151 local_bounds.set_size(track_size); |
| 152 return local_bounds; |
| 153 } |
| 154 |
| 155 ////////////////////////////////////////////////////////////////// |
| 156 // CocoaScrollBar, ScrollBar: |
| 157 |
| 158 int CocoaScrollBar::GetLayoutSize() const { |
| 159 return scroller_style_ == NSScrollerStyleOverlay ? 0 : kScrollbarWidth; |
| 160 } |
| 161 |
| 162 int CocoaScrollBar::GetContentOverlapSize() const { |
| 163 return scroller_style_ == NSScrollerStyleLegacy ? 0 : kScrollbarWidth; |
| 164 } |
| 165 |
| 166 ////////////////////////////////////////////////////////////////// |
| 167 // CocoaScrollBar::Views: |
| 168 |
| 169 gfx::Size CocoaScrollBar::GetPreferredSize() const { |
| 170 return gfx::Size(); |
| 171 } |
| 172 |
| 173 void CocoaScrollBar::OnPaint(gfx::Canvas* canvas) { |
| 174 if (!has_scrolltrack_) |
| 175 return; |
| 176 |
| 177 // Paint the scrollbar track background. |
| 178 gfx::Rect track_rect = GetLocalBounds(); |
| 179 |
| 180 SkPoint gradient_bounds[2]; |
| 181 if (IsHorizontal()) { |
| 182 gradient_bounds[0].set(track_rect.x(), track_rect.y()); |
| 183 gradient_bounds[1].set(track_rect.x(), track_rect.bottom()); |
| 184 } else { |
| 185 gradient_bounds[0].set(track_rect.x(), track_rect.y()); |
| 186 gradient_bounds[1].set(track_rect.right(), track_rect.y()); |
| 187 } |
| 188 skia::RefPtr<SkShader> shader = skia::AdoptRef(SkGradientShader::CreateLinear( |
| 189 gradient_bounds, kScrollerTrackGradientColors, nullptr, |
| 190 arraysize(kScrollerTrackGradientColors), SkShader::kClamp_TileMode)); |
| 191 SkPaint gradient; |
| 192 gradient.setShader(shader.get()); |
| 193 canvas->DrawRect(track_rect, gradient); |
| 194 |
| 195 // Draw the inner border: top if horizontal, left if vertical. |
| 196 SkPaint paint; |
| 197 paint.setColor(kScrollerTrackInnerBorderColor); |
| 198 gfx::Rect inner_border(track_rect); |
| 199 if (IsHorizontal()) |
| 200 inner_border.set_height(kScrollerTrackBorderWidth); |
| 201 else |
| 202 inner_border.set_width(kScrollerTrackBorderWidth); |
| 203 canvas->DrawRect(inner_border, paint); |
| 204 |
| 205 // Draw the outer border: bottom if horizontal, right if veritcal. |
| 206 paint.setColor(kScrollerTrackOuterBorderColor); |
| 207 gfx::Rect outer_border(inner_border); |
| 208 if (IsHorizontal()) |
| 209 outer_border.set_y(track_rect.bottom()); |
| 210 else |
| 211 outer_border.set_x(track_rect.right()); |
| 212 canvas->DrawRect(outer_border, paint); |
| 213 } |
| 214 |
| 215 void CocoaScrollBar::OnMouseEnteredScrollbarThumb(const ui::MouseEvent& event) { |
| 216 if (scroller_style_ != NSScrollerStyleOverlay) |
| 217 return; |
| 218 |
| 219 // If the scrollbar thumb has not compeletely faded away, then reshow it when |
| 220 // the mouse enters the scrollbar thumb. |
| 221 if (layer()->opacity()) |
| 222 ShowScrollbar(); |
| 223 |
| 224 hide_scrollbar_timer_.Reset(); |
| 225 } |
| 226 |
| 227 ////////////////////////////////////////////////////////////////// |
| 228 // CocoaScrollBar::ScrollDelegate: |
| 229 |
| 230 bool CocoaScrollBar::OnScroll(float dx, float dy) { |
| 231 bool did_scroll = BaseScrollBar::OnScroll(dx, dy); |
| 232 if (did_scroll && scroller_style_ == NSScrollerStyleOverlay) { |
| 233 ShowScrollbar(); |
| 234 hide_scrollbar_timer_.Reset(); |
| 235 } |
| 236 return did_scroll; |
| 237 } |
| 238 |
| 239 ////////////////////////////////////////////////////////////////// |
| 240 // CocoaScrollBar::ViewsScrollbarBridge: |
| 241 |
| 242 void CocoaScrollBar::OnScrollerStyleChanged() { |
| 243 NSScrollerStyle scroller_style = |
| 244 [ViewsScrollbarBridge getPreferredScrollerStyle]; |
| 245 if (scroller_style_ == scroller_style) |
| 246 return; |
| 247 |
| 248 scroller_style_ = scroller_style; |
| 249 |
| 250 // Ensure that the ScrollView updates the scrollbar's layout. |
| 251 if (parent()) |
| 252 parent()->Layout(); |
| 253 |
| 254 if (scroller_style_ == NSScrollerStyleOverlay) |
| 255 HideScrollbar(); |
| 256 else |
| 257 ShowScrollbar(); |
| 258 } |
| 259 |
| 260 ////////////////////////////////////////////////////////////////// |
| 261 // CocoaScrollBar, private: |
| 262 |
| 263 void CocoaScrollBar::HideScrollbar() { |
| 264 DCHECK(scroller_style_ == NSScrollerStyleOverlay); |
| 265 |
| 266 // If the thumb is in a hover or pressed state, we don't want the scrollbar |
| 267 // to disappear. As such, we should reset the timer. |
| 268 CocoaScrollBarThumb* thumb = static_cast<CocoaScrollBarThumb*>(GetThumb()); |
| 269 if (thumb->IsStateHoverOrPressed()) { |
| 270 hide_scrollbar_timer_.Reset(); |
| 271 return; |
| 272 } |
| 273 |
| 274 ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator()); |
| 275 animation.SetTransitionDuration( |
| 276 base::TimeDelta::FromMilliseconds(kFadeDurationMs)); |
| 277 layer()->SetOpacity(0.0); |
| 278 } |
| 279 |
| 280 void CocoaScrollBar::ShowScrollbar() { |
| 281 // Updates the scrolltrack and repaint it, if necessary. |
| 282 CocoaScrollBarThumb* thumb = static_cast<CocoaScrollBarThumb*>(GetThumb()); |
| 283 bool has_scrolltrack = scroller_style_ == NSScrollerStyleLegacy || |
| 284 thumb->IsStateHoverOrPressed(); |
| 285 if (has_scrolltrack_ != has_scrolltrack) { |
| 286 has_scrolltrack_ = has_scrolltrack; |
| 287 SchedulePaint(); |
| 288 } |
| 289 |
| 290 layer()->SetOpacity(1.0); |
| 291 hide_scrollbar_timer_.Stop(); |
| 292 } |
| 293 |
| 294 } // namespace views |
| OLD | NEW |