1// Copyright (c) 2012 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#include "ui/views/controls/scrollbar/native_scroll_bar_views.h" 6 7#include "base/logging.h" 8#include "ui/events/keycodes/keyboard_codes.h" 9#include "ui/gfx/canvas.h" 10#include "ui/gfx/path.h" 11#include "ui/views/controls/button/custom_button.h" 12#include "ui/views/controls/focusable_border.h" 13#include "ui/views/controls/scrollbar/base_scroll_bar_button.h" 14#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h" 15#include "ui/views/controls/scrollbar/native_scroll_bar.h" 16#include "ui/views/controls/scrollbar/scroll_bar.h" 17 18namespace views { 19 20namespace { 21 22// Wrapper for the scroll buttons. 23class ScrollBarButton : public BaseScrollBarButton { 24 public: 25 enum Type { 26 UP, 27 DOWN, 28 LEFT, 29 RIGHT, 30 }; 31 32 ScrollBarButton(ButtonListener* listener, Type type); 33 virtual ~ScrollBarButton(); 34 35 virtual gfx::Size GetPreferredSize() const OVERRIDE; 36 virtual const char* GetClassName() const OVERRIDE { 37 return "ScrollBarButton"; 38 } 39 40 protected: 41 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; 42 43 private: 44 ui::NativeTheme::ExtraParams GetNativeThemeParams() const; 45 ui::NativeTheme::Part GetNativeThemePart() const; 46 ui::NativeTheme::State GetNativeThemeState() const; 47 48 Type type_; 49}; 50 51// Wrapper for the scroll thumb 52class ScrollBarThumb : public BaseScrollBarThumb { 53 public: 54 explicit ScrollBarThumb(BaseScrollBar* scroll_bar); 55 virtual ~ScrollBarThumb(); 56 57 virtual gfx::Size GetPreferredSize() const OVERRIDE; 58 virtual const char* GetClassName() const OVERRIDE { 59 return "ScrollBarThumb"; 60 } 61 62 protected: 63 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; 64 65 private: 66 ui::NativeTheme::ExtraParams GetNativeThemeParams() const; 67 ui::NativeTheme::Part GetNativeThemePart() const; 68 ui::NativeTheme::State GetNativeThemeState() const; 69 70 ScrollBar* scroll_bar_; 71}; 72 73///////////////////////////////////////////////////////////////////////////// 74// ScrollBarButton 75 76ScrollBarButton::ScrollBarButton(ButtonListener* listener, Type type) 77 : BaseScrollBarButton(listener), 78 type_(type) { 79 SetFocusable(false); 80 SetAccessibilityFocusable(false); 81} 82 83ScrollBarButton::~ScrollBarButton() { 84} 85 86gfx::Size ScrollBarButton::GetPreferredSize() const { 87 return GetNativeTheme()->GetPartSize(GetNativeThemePart(), 88 GetNativeThemeState(), 89 GetNativeThemeParams()); 90} 91 92void ScrollBarButton::OnPaint(gfx::Canvas* canvas) { 93 gfx::Rect bounds(GetPreferredSize()); 94 GetNativeTheme()->Paint(canvas->sk_canvas(), GetNativeThemePart(), 95 GetNativeThemeState(), bounds, 96 GetNativeThemeParams()); 97} 98 99ui::NativeTheme::ExtraParams 100 ScrollBarButton::GetNativeThemeParams() const { 101 ui::NativeTheme::ExtraParams params; 102 103 switch (state_) { 104 case CustomButton::STATE_HOVERED: 105 params.scrollbar_arrow.is_hovering = true; 106 break; 107 default: 108 params.scrollbar_arrow.is_hovering = false; 109 break; 110 } 111 112 return params; 113} 114 115ui::NativeTheme::Part 116 ScrollBarButton::GetNativeThemePart() const { 117 switch (type_) { 118 case UP: 119 return ui::NativeTheme::kScrollbarUpArrow; 120 case DOWN: 121 return ui::NativeTheme::kScrollbarDownArrow; 122 case LEFT: 123 return ui::NativeTheme::kScrollbarLeftArrow; 124 case RIGHT: 125 return ui::NativeTheme::kScrollbarRightArrow; 126 default: 127 return ui::NativeTheme::kScrollbarUpArrow; 128 } 129} 130 131ui::NativeTheme::State 132 ScrollBarButton::GetNativeThemeState() const { 133 ui::NativeTheme::State state; 134 135 switch (state_) { 136 case CustomButton::STATE_HOVERED: 137 state = ui::NativeTheme::kHovered; 138 break; 139 case CustomButton::STATE_PRESSED: 140 state = ui::NativeTheme::kPressed; 141 break; 142 case CustomButton::STATE_DISABLED: 143 state = ui::NativeTheme::kDisabled; 144 break; 145 case CustomButton::STATE_NORMAL: 146 default: 147 state = ui::NativeTheme::kNormal; 148 break; 149 } 150 151 return state; 152} 153 154///////////////////////////////////////////////////////////////////////////// 155// ScrollBarThumb 156 157ScrollBarThumb::ScrollBarThumb(BaseScrollBar* scroll_bar) 158 : BaseScrollBarThumb(scroll_bar), 159 scroll_bar_(scroll_bar) { 160 SetFocusable(false); 161 SetAccessibilityFocusable(false); 162} 163 164ScrollBarThumb::~ScrollBarThumb() { 165} 166 167gfx::Size ScrollBarThumb::GetPreferredSize() const { 168 return GetNativeTheme()->GetPartSize(GetNativeThemePart(), 169 GetNativeThemeState(), 170 GetNativeThemeParams()); 171} 172 173void ScrollBarThumb::OnPaint(gfx::Canvas* canvas) { 174 const gfx::Rect local_bounds(GetLocalBounds()); 175 const ui::NativeTheme::State theme_state = GetNativeThemeState(); 176 const ui::NativeTheme::ExtraParams extra_params(GetNativeThemeParams()); 177 GetNativeTheme()->Paint(canvas->sk_canvas(), 178 GetNativeThemePart(), 179 theme_state, 180 local_bounds, 181 extra_params); 182 const ui::NativeTheme::Part gripper_part = scroll_bar_->IsHorizontal() ? 183 ui::NativeTheme::kScrollbarHorizontalGripper : 184 ui::NativeTheme::kScrollbarVerticalGripper; 185 GetNativeTheme()->Paint(canvas->sk_canvas(), gripper_part, theme_state, 186 local_bounds, extra_params); 187} 188 189ui::NativeTheme::ExtraParams ScrollBarThumb::GetNativeThemeParams() const { 190 // This gives the behavior we want. 191 ui::NativeTheme::ExtraParams params; 192 params.scrollbar_thumb.is_hovering = 193 (GetState() != CustomButton::STATE_HOVERED); 194 return params; 195} 196 197ui::NativeTheme::Part ScrollBarThumb::GetNativeThemePart() const { 198 if (scroll_bar_->IsHorizontal()) 199 return ui::NativeTheme::kScrollbarHorizontalThumb; 200 return ui::NativeTheme::kScrollbarVerticalThumb; 201} 202 203ui::NativeTheme::State ScrollBarThumb::GetNativeThemeState() const { 204 ui::NativeTheme::State state; 205 206 switch (GetState()) { 207 case CustomButton::STATE_HOVERED: 208 state = ui::NativeTheme::kHovered; 209 break; 210 case CustomButton::STATE_PRESSED: 211 state = ui::NativeTheme::kPressed; 212 break; 213 case CustomButton::STATE_DISABLED: 214 state = ui::NativeTheme::kDisabled; 215 break; 216 case CustomButton::STATE_NORMAL: 217 default: 218 state = ui::NativeTheme::kNormal; 219 break; 220 } 221 222 return state; 223} 224 225} // namespace 226 227//////////////////////////////////////////////////////////////////////////////// 228// NativeScrollBarViews, public: 229 230const char NativeScrollBarViews::kViewClassName[] = "NativeScrollBarViews"; 231 232NativeScrollBarViews::NativeScrollBarViews(NativeScrollBar* scroll_bar) 233 : BaseScrollBar(scroll_bar->IsHorizontal(), 234 new ScrollBarThumb(this)), 235 native_scroll_bar_(scroll_bar) { 236 set_controller(native_scroll_bar_->controller()); 237 238 if (native_scroll_bar_->IsHorizontal()) { 239 prev_button_ = new ScrollBarButton(this, ScrollBarButton::LEFT); 240 next_button_ = new ScrollBarButton(this, ScrollBarButton::RIGHT); 241 242 part_ = ui::NativeTheme::kScrollbarHorizontalTrack; 243 } else { 244 prev_button_ = new ScrollBarButton(this, ScrollBarButton::UP); 245 next_button_ = new ScrollBarButton(this, ScrollBarButton::DOWN); 246 247 part_ = ui::NativeTheme::kScrollbarVerticalTrack; 248 } 249 250 state_ = ui::NativeTheme::kNormal; 251 252 AddChildView(prev_button_); 253 AddChildView(next_button_); 254 255 prev_button_->set_context_menu_controller(this); 256 next_button_->set_context_menu_controller(this); 257} 258 259NativeScrollBarViews::~NativeScrollBarViews() { 260} 261 262//////////////////////////////////////////////////////////////////////////////// 263// NativeScrollBarViews, View overrides: 264 265void NativeScrollBarViews::Layout() { 266 gfx::Size size = prev_button_->GetPreferredSize(); 267 prev_button_->SetBounds(0, 0, size.width(), size.height()); 268 269 if (native_scroll_bar_->IsHorizontal()) { 270 next_button_->SetBounds(width() - size.width(), 0, 271 size.width(), size.height()); 272 } else { 273 next_button_->SetBounds(0, height() - size.height(), 274 size.width(), size.height()); 275 } 276 277 GetThumb()->SetBoundsRect(GetTrackBounds()); 278} 279 280void NativeScrollBarViews::OnPaint(gfx::Canvas* canvas) { 281 gfx::Rect bounds = GetTrackBounds(); 282 283 if (bounds.IsEmpty()) 284 return; 285 286 params_.scrollbar_track.track_x = bounds.x(); 287 params_.scrollbar_track.track_y = bounds.y(); 288 params_.scrollbar_track.track_width = bounds.width(); 289 params_.scrollbar_track.track_height = bounds.height(); 290 params_.scrollbar_track.classic_state = 0; 291 292 GetNativeTheme()->Paint(canvas->sk_canvas(), part_, state_, bounds, params_); 293} 294 295gfx::Size NativeScrollBarViews::GetPreferredSize() const { 296 const ui::NativeTheme* theme = native_scroll_bar_->GetNativeTheme(); 297 if (native_scroll_bar_->IsHorizontal()) 298 return gfx::Size(0, GetHorizontalScrollBarHeight(theme)); 299 return gfx::Size(GetVerticalScrollBarWidth(theme), 0); 300} 301 302const char* NativeScrollBarViews::GetClassName() const { 303 return kViewClassName; 304} 305 306int NativeScrollBarViews::GetLayoutSize() const { 307 gfx::Size size = prev_button_->GetPreferredSize(); 308 return IsHorizontal() ? size.height() : size.width(); 309} 310 311void NativeScrollBarViews::ScrollToPosition(int position) { 312 controller()->ScrollToPosition(native_scroll_bar_, position); 313} 314 315int NativeScrollBarViews::GetScrollIncrement(bool is_page, bool is_positive) { 316 return controller()->GetScrollIncrement(native_scroll_bar_, 317 is_page, 318 is_positive); 319} 320 321////////////////////////////////////////////////////////////////////////////// 322// BaseButton::ButtonListener overrides: 323 324void NativeScrollBarViews::ButtonPressed(Button* sender, 325 const ui::Event& event) { 326 if (sender == prev_button_) { 327 ScrollByAmount(SCROLL_PREV_LINE); 328 } else if (sender == next_button_) { 329 ScrollByAmount(SCROLL_NEXT_LINE); 330 } 331} 332 333//////////////////////////////////////////////////////////////////////////////// 334// NativeScrollBarViews, NativeScrollBarWrapper overrides: 335 336int NativeScrollBarViews::GetPosition() const { 337 return BaseScrollBar::GetPosition(); 338} 339 340View* NativeScrollBarViews::GetView() { 341 return this; 342} 343 344void NativeScrollBarViews::Update(int viewport_size, 345 int content_size, 346 int current_pos) { 347 BaseScrollBar::Update(viewport_size, content_size, current_pos); 348} 349 350//////////////////////////////////////////////////////////////////////////////// 351// NativeScrollBarViews, private: 352 353gfx::Rect NativeScrollBarViews::GetTrackBounds() const { 354 gfx::Rect bounds = GetLocalBounds(); 355 gfx::Size size = prev_button_->GetPreferredSize(); 356 BaseScrollBarThumb* thumb = GetThumb(); 357 358 if (native_scroll_bar_->IsHorizontal()) { 359 bounds.set_x(bounds.x() + size.width()); 360 bounds.set_width(std::max(0, bounds.width() - 2 * size.width())); 361 bounds.set_height(thumb->GetPreferredSize().height()); 362 } else { 363 bounds.set_y(bounds.y() + size.height()); 364 bounds.set_height(std::max(0, bounds.height() - 2 * size.height())); 365 bounds.set_width(thumb->GetPreferredSize().width()); 366 } 367 368 return bounds; 369} 370 371//////////////////////////////////////////////////////////////////////////////// 372// NativewScrollBarWrapper, public: 373 374// static 375NativeScrollBarWrapper* NativeScrollBarWrapper::CreateWrapper( 376 NativeScrollBar* scroll_bar) { 377 return new NativeScrollBarViews(scroll_bar); 378} 379 380// static 381int NativeScrollBarWrapper::GetHorizontalScrollBarHeight( 382 const ui::NativeTheme* theme) { 383 if (!theme) 384 theme = ui::NativeTheme::instance(); 385 ui::NativeTheme::ExtraParams button_params; 386 button_params.scrollbar_arrow.is_hovering = false; 387 gfx::Size button_size = theme->GetPartSize( 388 ui::NativeTheme::kScrollbarLeftArrow, 389 ui::NativeTheme::kNormal, 390 button_params); 391 392 ui::NativeTheme::ExtraParams thumb_params; 393 thumb_params.scrollbar_thumb.is_hovering = false; 394 gfx::Size track_size = theme->GetPartSize( 395 ui::NativeTheme::kScrollbarHorizontalThumb, 396 ui::NativeTheme::kNormal, 397 thumb_params); 398 399 return std::max(track_size.height(), button_size.height()); 400} 401 402// static 403int NativeScrollBarWrapper::GetVerticalScrollBarWidth( 404 const ui::NativeTheme* theme) { 405 if (!theme) 406 theme = ui::NativeTheme::instance(); 407 ui::NativeTheme::ExtraParams button_params; 408 button_params.scrollbar_arrow.is_hovering = false; 409 gfx::Size button_size = theme->GetPartSize( 410 ui::NativeTheme::kScrollbarUpArrow, 411 ui::NativeTheme::kNormal, 412 button_params); 413 414 ui::NativeTheme::ExtraParams thumb_params; 415 thumb_params.scrollbar_thumb.is_hovering = false; 416 gfx::Size track_size = theme->GetPartSize( 417 ui::NativeTheme::kScrollbarVerticalThumb, 418 ui::NativeTheme::kNormal, 419 thumb_params); 420 421 return std::max(track_size.width(), button_size.width()); 422} 423 424} // namespace views 425