Home | History | Annotate | Download | only in scrollbar
      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/base/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 
     18 namespace views {
     19 
     20 namespace {
     21 
     22 // Wrapper for the scroll buttons.
     23 class 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() 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
     52 class ScrollBarThumb : public BaseScrollBarThumb {
     53  public:
     54   explicit ScrollBarThumb(BaseScrollBar* scroll_bar);
     55   virtual ~ScrollBarThumb();
     56 
     57   virtual gfx::Size GetPreferredSize() 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 
     76 ScrollBarButton::ScrollBarButton(ButtonListener* listener, Type type)
     77     : BaseScrollBarButton(listener),
     78       type_(type) {
     79   set_focusable(false);
     80   set_accessibility_focusable(false);
     81 }
     82 
     83 ScrollBarButton::~ScrollBarButton() {
     84 }
     85 
     86 gfx::Size ScrollBarButton::GetPreferredSize() {
     87   return GetNativeTheme()->GetPartSize(GetNativeThemePart(),
     88                                        GetNativeThemeState(),
     89                                        GetNativeThemeParams());
     90 }
     91 
     92 void ScrollBarButton::OnPaint(gfx::Canvas* canvas) {
     93   gfx::Rect bounds(GetPreferredSize());
     94   GetNativeTheme()->Paint(canvas->sk_canvas(), GetNativeThemePart(),
     95                           GetNativeThemeState(), bounds,
     96                           GetNativeThemeParams());
     97 }
     98 
     99 ui::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 
    115 ui::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 
    131 ui::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 
    157 ScrollBarThumb::ScrollBarThumb(BaseScrollBar* scroll_bar)
    158     : BaseScrollBarThumb(scroll_bar),
    159       scroll_bar_(scroll_bar) {
    160   set_focusable(false);
    161   set_accessibility_focusable(false);
    162 }
    163 
    164 ScrollBarThumb::~ScrollBarThumb() {
    165 }
    166 
    167 gfx::Size ScrollBarThumb::GetPreferredSize() {
    168   return GetNativeTheme()->GetPartSize(GetNativeThemePart(),
    169                                        GetNativeThemeState(),
    170                                        GetNativeThemeParams());
    171 }
    172 
    173 void 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 
    189 ui::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 
    197 ui::NativeTheme::Part ScrollBarThumb::GetNativeThemePart() const {
    198   if (scroll_bar_->IsHorizontal())
    199     return ui::NativeTheme::kScrollbarHorizontalThumb;
    200   return ui::NativeTheme::kScrollbarVerticalThumb;
    201 }
    202 
    203 ui::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 
    230 const char NativeScrollBarViews::kViewClassName[] = "NativeScrollBarViews";
    231 
    232 NativeScrollBarViews::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 
    259 NativeScrollBarViews::~NativeScrollBarViews() {
    260 }
    261 
    262 ////////////////////////////////////////////////////////////////////////////////
    263 // NativeScrollBarViews, View overrides:
    264 
    265 void 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 
    280 void 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 
    295 gfx::Size NativeScrollBarViews::GetPreferredSize() {
    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 
    302 const char* NativeScrollBarViews::GetClassName() const {
    303   return kViewClassName;
    304 }
    305 
    306 int NativeScrollBarViews::GetLayoutSize() const {
    307   gfx::Size size = prev_button_->GetPreferredSize();
    308   return IsHorizontal() ? size.height() : size.width();
    309 }
    310 
    311 void NativeScrollBarViews::ScrollToPosition(int position) {
    312   controller()->ScrollToPosition(native_scroll_bar_, position);
    313 }
    314 
    315 int 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 
    324 void 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 
    336 int NativeScrollBarViews::GetPosition() const {
    337   return BaseScrollBar::GetPosition();
    338 }
    339 
    340 View* NativeScrollBarViews::GetView() {
    341   return this;
    342 }
    343 
    344 void 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 
    353 gfx::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
    375 NativeScrollBarWrapper* NativeScrollBarWrapper::CreateWrapper(
    376     NativeScrollBar* scroll_bar) {
    377   return new NativeScrollBarViews(scroll_bar);
    378 }
    379 
    380 // static
    381 int 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
    403 int 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