root/ui/views/controls/scrollbar/native_scroll_bar_views.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetClassName
  2. GetClassName
  3. type_
  4. GetPreferredSize
  5. OnPaint
  6. GetNativeThemeParams
  7. GetNativeThemePart
  8. GetNativeThemeState
  9. scroll_bar_
  10. GetPreferredSize
  11. OnPaint
  12. GetNativeThemeParams
  13. GetNativeThemePart
  14. GetNativeThemeState
  15. native_scroll_bar_
  16. Layout
  17. OnPaint
  18. GetPreferredSize
  19. GetClassName
  20. GetLayoutSize
  21. ScrollToPosition
  22. GetScrollIncrement
  23. ButtonPressed
  24. GetPosition
  25. GetView
  26. Update
  27. GetTrackBounds
  28. CreateWrapper
  29. GetHorizontalScrollBarHeight
  30. GetVerticalScrollBarWidth

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/scrollbar/native_scroll_bar_views.h"

#include "base/logging.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/path.h"
#include "ui/views/controls/button/custom_button.h"
#include "ui/views/controls/focusable_border.h"
#include "ui/views/controls/scrollbar/base_scroll_bar_button.h"
#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h"
#include "ui/views/controls/scrollbar/native_scroll_bar.h"
#include "ui/views/controls/scrollbar/scroll_bar.h"

namespace views {

namespace {

// Wrapper for the scroll buttons.
class ScrollBarButton : public BaseScrollBarButton {
 public:
  enum Type {
    UP,
    DOWN,
    LEFT,
    RIGHT,
  };

  ScrollBarButton(ButtonListener* listener, Type type);
  virtual ~ScrollBarButton();

  virtual gfx::Size GetPreferredSize() OVERRIDE;
  virtual const char* GetClassName() const OVERRIDE {
    return "ScrollBarButton";
  }

 protected:
  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;

 private:
  ui::NativeTheme::ExtraParams GetNativeThemeParams() const;
  ui::NativeTheme::Part GetNativeThemePart() const;
  ui::NativeTheme::State GetNativeThemeState() const;

  Type type_;
};

// Wrapper for the scroll thumb
class ScrollBarThumb : public BaseScrollBarThumb {
 public:
  explicit ScrollBarThumb(BaseScrollBar* scroll_bar);
  virtual ~ScrollBarThumb();

  virtual gfx::Size GetPreferredSize() OVERRIDE;
  virtual const char* GetClassName() const OVERRIDE {
    return "ScrollBarThumb";
  }

 protected:
  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;

 private:
  ui::NativeTheme::ExtraParams GetNativeThemeParams() const;
  ui::NativeTheme::Part GetNativeThemePart() const;
  ui::NativeTheme::State GetNativeThemeState() const;

  ScrollBar* scroll_bar_;
};

/////////////////////////////////////////////////////////////////////////////
// ScrollBarButton

ScrollBarButton::ScrollBarButton(ButtonListener* listener, Type type)
    : BaseScrollBarButton(listener),
      type_(type) {
  SetFocusable(false);
  SetAccessibilityFocusable(false);
}

ScrollBarButton::~ScrollBarButton() {
}

gfx::Size ScrollBarButton::GetPreferredSize() {
  return GetNativeTheme()->GetPartSize(GetNativeThemePart(),
                                       GetNativeThemeState(),
                                       GetNativeThemeParams());
}

void ScrollBarButton::OnPaint(gfx::Canvas* canvas) {
  gfx::Rect bounds(GetPreferredSize());
  GetNativeTheme()->Paint(canvas->sk_canvas(), GetNativeThemePart(),
                          GetNativeThemeState(), bounds,
                          GetNativeThemeParams());
}

ui::NativeTheme::ExtraParams
    ScrollBarButton::GetNativeThemeParams() const {
  ui::NativeTheme::ExtraParams params;

  switch (state_) {
    case CustomButton::STATE_HOVERED:
      params.scrollbar_arrow.is_hovering = true;
      break;
    default:
      params.scrollbar_arrow.is_hovering = false;
      break;
  }

  return params;
}

ui::NativeTheme::Part
    ScrollBarButton::GetNativeThemePart() const {
  switch (type_) {
    case UP:
      return ui::NativeTheme::kScrollbarUpArrow;
    case DOWN:
      return ui::NativeTheme::kScrollbarDownArrow;
    case LEFT:
      return ui::NativeTheme::kScrollbarLeftArrow;
    case RIGHT:
      return ui::NativeTheme::kScrollbarRightArrow;
    default:
      return ui::NativeTheme::kScrollbarUpArrow;
  }
}

ui::NativeTheme::State
    ScrollBarButton::GetNativeThemeState() const {
  ui::NativeTheme::State state;

  switch (state_) {
    case CustomButton::STATE_HOVERED:
      state = ui::NativeTheme::kHovered;
      break;
    case CustomButton::STATE_PRESSED:
      state = ui::NativeTheme::kPressed;
      break;
    case CustomButton::STATE_DISABLED:
      state = ui::NativeTheme::kDisabled;
      break;
    case CustomButton::STATE_NORMAL:
    default:
      state = ui::NativeTheme::kNormal;
      break;
  }

  return state;
}

/////////////////////////////////////////////////////////////////////////////
// ScrollBarThumb

ScrollBarThumb::ScrollBarThumb(BaseScrollBar* scroll_bar)
    : BaseScrollBarThumb(scroll_bar),
      scroll_bar_(scroll_bar) {
  SetFocusable(false);
  SetAccessibilityFocusable(false);
}

ScrollBarThumb::~ScrollBarThumb() {
}

gfx::Size ScrollBarThumb::GetPreferredSize() {
  return GetNativeTheme()->GetPartSize(GetNativeThemePart(),
                                       GetNativeThemeState(),
                                       GetNativeThemeParams());
}

void ScrollBarThumb::OnPaint(gfx::Canvas* canvas) {
  const gfx::Rect local_bounds(GetLocalBounds());
  const ui::NativeTheme::State theme_state = GetNativeThemeState();
  const ui::NativeTheme::ExtraParams extra_params(GetNativeThemeParams());
  GetNativeTheme()->Paint(canvas->sk_canvas(),
                          GetNativeThemePart(),
                          theme_state,
                          local_bounds,
                          extra_params);
  const ui::NativeTheme::Part gripper_part = scroll_bar_->IsHorizontal() ?
      ui::NativeTheme::kScrollbarHorizontalGripper :
      ui::NativeTheme::kScrollbarVerticalGripper;
  GetNativeTheme()->Paint(canvas->sk_canvas(), gripper_part, theme_state,
                          local_bounds, extra_params);
}

ui::NativeTheme::ExtraParams ScrollBarThumb::GetNativeThemeParams() const {
  // This gives the behavior we want.
  ui::NativeTheme::ExtraParams params;
  params.scrollbar_thumb.is_hovering =
      (GetState() != CustomButton::STATE_HOVERED);
  return params;
}

ui::NativeTheme::Part ScrollBarThumb::GetNativeThemePart() const {
  if (scroll_bar_->IsHorizontal())
    return ui::NativeTheme::kScrollbarHorizontalThumb;
  return ui::NativeTheme::kScrollbarVerticalThumb;
}

ui::NativeTheme::State ScrollBarThumb::GetNativeThemeState() const {
  ui::NativeTheme::State state;

  switch (GetState()) {
    case CustomButton::STATE_HOVERED:
      state = ui::NativeTheme::kHovered;
      break;
    case CustomButton::STATE_PRESSED:
      state = ui::NativeTheme::kPressed;
      break;
    case CustomButton::STATE_DISABLED:
      state = ui::NativeTheme::kDisabled;
      break;
    case CustomButton::STATE_NORMAL:
    default:
      state = ui::NativeTheme::kNormal;
      break;
  }

  return state;
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, public:

const char NativeScrollBarViews::kViewClassName[] = "NativeScrollBarViews";

NativeScrollBarViews::NativeScrollBarViews(NativeScrollBar* scroll_bar)
    : BaseScrollBar(scroll_bar->IsHorizontal(),
                    new ScrollBarThumb(this)),
      native_scroll_bar_(scroll_bar) {
  set_controller(native_scroll_bar_->controller());

  if (native_scroll_bar_->IsHorizontal()) {
    prev_button_ = new ScrollBarButton(this, ScrollBarButton::LEFT);
    next_button_ = new ScrollBarButton(this, ScrollBarButton::RIGHT);

    part_ = ui::NativeTheme::kScrollbarHorizontalTrack;
  } else {
    prev_button_ = new ScrollBarButton(this, ScrollBarButton::UP);
    next_button_ = new ScrollBarButton(this, ScrollBarButton::DOWN);

    part_ = ui::NativeTheme::kScrollbarVerticalTrack;
  }

  state_ = ui::NativeTheme::kNormal;

  AddChildView(prev_button_);
  AddChildView(next_button_);

  prev_button_->set_context_menu_controller(this);
  next_button_->set_context_menu_controller(this);
}

NativeScrollBarViews::~NativeScrollBarViews() {
}

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, View overrides:

void NativeScrollBarViews::Layout() {
  gfx::Size size = prev_button_->GetPreferredSize();
  prev_button_->SetBounds(0, 0, size.width(), size.height());

  if (native_scroll_bar_->IsHorizontal()) {
    next_button_->SetBounds(width() - size.width(), 0,
                            size.width(), size.height());
  } else {
    next_button_->SetBounds(0, height() - size.height(),
                            size.width(), size.height());
  }

  GetThumb()->SetBoundsRect(GetTrackBounds());
}

void NativeScrollBarViews::OnPaint(gfx::Canvas* canvas) {
  gfx::Rect bounds = GetTrackBounds();

  if (bounds.IsEmpty())
    return;

  params_.scrollbar_track.track_x = bounds.x();
  params_.scrollbar_track.track_y = bounds.y();
  params_.scrollbar_track.track_width = bounds.width();
  params_.scrollbar_track.track_height = bounds.height();
  params_.scrollbar_track.classic_state = 0;

  GetNativeTheme()->Paint(canvas->sk_canvas(), part_, state_, bounds, params_);
}

gfx::Size NativeScrollBarViews::GetPreferredSize() {
  const ui::NativeTheme* theme = native_scroll_bar_->GetNativeTheme();
  if (native_scroll_bar_->IsHorizontal())
    return gfx::Size(0, GetHorizontalScrollBarHeight(theme));
  return gfx::Size(GetVerticalScrollBarWidth(theme), 0);
}

const char* NativeScrollBarViews::GetClassName() const {
  return kViewClassName;
}

int NativeScrollBarViews::GetLayoutSize() const {
  gfx::Size size = prev_button_->GetPreferredSize();
  return IsHorizontal() ? size.height() : size.width();
}

void NativeScrollBarViews::ScrollToPosition(int position) {
  controller()->ScrollToPosition(native_scroll_bar_, position);
}

int NativeScrollBarViews::GetScrollIncrement(bool is_page, bool is_positive) {
  return controller()->GetScrollIncrement(native_scroll_bar_,
                                          is_page,
                                          is_positive);
}

//////////////////////////////////////////////////////////////////////////////
// BaseButton::ButtonListener overrides:

void NativeScrollBarViews::ButtonPressed(Button* sender,
                                         const ui::Event& event) {
  if (sender == prev_button_) {
    ScrollByAmount(SCROLL_PREV_LINE);
  } else if (sender == next_button_) {
    ScrollByAmount(SCROLL_NEXT_LINE);
  }
}

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, NativeScrollBarWrapper overrides:

int NativeScrollBarViews::GetPosition() const {
  return BaseScrollBar::GetPosition();
}

View* NativeScrollBarViews::GetView() {
  return this;
}

void NativeScrollBarViews::Update(int viewport_size,
                                  int content_size,
                                  int current_pos) {
  BaseScrollBar::Update(viewport_size, content_size, current_pos);
}

////////////////////////////////////////////////////////////////////////////////
// NativeScrollBarViews, private:

gfx::Rect NativeScrollBarViews::GetTrackBounds() const {
  gfx::Rect bounds = GetLocalBounds();
  gfx::Size size = prev_button_->GetPreferredSize();
  BaseScrollBarThumb* thumb = GetThumb();

  if (native_scroll_bar_->IsHorizontal()) {
    bounds.set_x(bounds.x() + size.width());
    bounds.set_width(std::max(0, bounds.width() - 2 * size.width()));
    bounds.set_height(thumb->GetPreferredSize().height());
  } else {
    bounds.set_y(bounds.y() + size.height());
    bounds.set_height(std::max(0, bounds.height() - 2 * size.height()));
    bounds.set_width(thumb->GetPreferredSize().width());
  }

  return bounds;
}

////////////////////////////////////////////////////////////////////////////////
// NativewScrollBarWrapper, public:

// static
NativeScrollBarWrapper* NativeScrollBarWrapper::CreateWrapper(
    NativeScrollBar* scroll_bar) {
  return new NativeScrollBarViews(scroll_bar);
}

// static
int NativeScrollBarWrapper::GetHorizontalScrollBarHeight(
    const ui::NativeTheme* theme) {
  if (!theme)
    theme = ui::NativeTheme::instance();
  ui::NativeTheme::ExtraParams button_params;
  button_params.scrollbar_arrow.is_hovering = false;
  gfx::Size button_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarLeftArrow,
      ui::NativeTheme::kNormal,
      button_params);

  ui::NativeTheme::ExtraParams thumb_params;
  thumb_params.scrollbar_thumb.is_hovering = false;
  gfx::Size track_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarHorizontalThumb,
      ui::NativeTheme::kNormal,
      thumb_params);

  return std::max(track_size.height(), button_size.height());
}

// static
int NativeScrollBarWrapper::GetVerticalScrollBarWidth(
    const ui::NativeTheme* theme) {
  if (!theme)
    theme = ui::NativeTheme::instance();
  ui::NativeTheme::ExtraParams button_params;
  button_params.scrollbar_arrow.is_hovering = false;
  gfx::Size button_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarUpArrow,
      ui::NativeTheme::kNormal,
      button_params);

  ui::NativeTheme::ExtraParams thumb_params;
  thumb_params.scrollbar_thumb.is_hovering = false;
  gfx::Size track_size = theme->GetPartSize(
      ui::NativeTheme::kScrollbarVerticalThumb,
      ui::NativeTheme::kNormal,
      thumb_params);

  return std::max(track_size.width(), button_size.width());
}

}  // namespace views

/* [<][>][^][v][top][bottom][index][help] */