summaryrefslogtreecommitdiffstats
path: root/views/controls/scrollbar
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-08 00:34:05 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-08 00:34:05 +0000
commit2362e4fe2905ab75d3230ebc3e307ae53e2b8362 (patch)
treee6d88357a2021811e0e354f618247217be8bb3da /views/controls/scrollbar
parentdb23ac3e713dc17509b2b15d3ee634968da45715 (diff)
downloadchromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.zip
chromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.tar.gz
chromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.tar.bz2
Move src/chrome/views to src/views. RS=darin http://crbug.com/11387
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15604 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/controls/scrollbar')
-rw-r--r--views/controls/scrollbar/bitmap_scroll_bar.cc703
-rw-r--r--views/controls/scrollbar/bitmap_scroll_bar.h192
-rw-r--r--views/controls/scrollbar/native_scroll_bar.cc357
-rw-r--r--views/controls/scrollbar/native_scroll_bar.h67
-rw-r--r--views/controls/scrollbar/scroll_bar.cc47
-rw-r--r--views/controls/scrollbar/scroll_bar.h102
6 files changed, 1468 insertions, 0 deletions
diff --git a/views/controls/scrollbar/bitmap_scroll_bar.cc b/views/controls/scrollbar/bitmap_scroll_bar.cc
new file mode 100644
index 0000000..4a93782
--- /dev/null
+++ b/views/controls/scrollbar/bitmap_scroll_bar.cc
@@ -0,0 +1,703 @@
+// Copyright (c) 2006-2008 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 "views/controls/scrollbar/bitmap_scroll_bar.h"
+
+#include "app/gfx/chrome_canvas.h"
+#include "app/l10n_util.h"
+#include "base/message_loop.h"
+#include "grit/generated_resources.h"
+#include "skia/include/SkBitmap.h"
+#include "views/controls/menu/menu.h"
+#include "views/controls/scroll_view.h"
+#include "views/widget/widget.h"
+
+#undef min
+#undef max
+
+namespace views {
+
+namespace {
+
+// The distance the mouse can be dragged outside the bounds of the thumb during
+// dragging before the scrollbar will snap back to its regular position.
+static const int kScrollThumbDragOutSnap = 100;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// AutorepeatButton
+//
+// A button that activates on mouse pressed rather than released, and that
+// continues to fire the clicked action as the mouse button remains pressed
+// down on the button.
+//
+///////////////////////////////////////////////////////////////////////////////
+class AutorepeatButton : public ImageButton {
+ public:
+ AutorepeatButton(ButtonListener* listener)
+ : ImageButton(listener),
+ repeater_(NewCallback<AutorepeatButton>(this,
+ &AutorepeatButton::NotifyClick)) {
+ }
+ virtual ~AutorepeatButton() {}
+
+ protected:
+ virtual bool OnMousePressed(const MouseEvent& event) {
+ Button::NotifyClick(event.GetFlags());
+ repeater_.Start();
+ return true;
+ }
+
+ virtual void OnMouseReleased(const MouseEvent& event, bool canceled) {
+ repeater_.Stop();
+ View::OnMouseReleased(event, canceled);
+ }
+
+ private:
+ void NotifyClick() {
+ Button::NotifyClick(0);
+ }
+
+ // The repeat controller that we use to repeatedly click the button when the
+ // mouse button is down.
+ RepeatController repeater_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AutorepeatButton);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// BitmapScrollBarThumb
+//
+// A view that acts as the thumb in the scroll bar track that the user can
+// drag to scroll the associated contents view within the viewport.
+//
+///////////////////////////////////////////////////////////////////////////////
+class BitmapScrollBarThumb : public View {
+ public:
+ explicit BitmapScrollBarThumb(BitmapScrollBar* scroll_bar)
+ : scroll_bar_(scroll_bar),
+ drag_start_position_(-1),
+ mouse_offset_(-1),
+ state_(CustomButton::BS_NORMAL) {
+ }
+ virtual ~BitmapScrollBarThumb() { }
+
+ // Sets the size (width or height) of the thumb to the specified value.
+ void SetSize(int size) {
+ // Make sure the thumb is never sized smaller than its minimum possible
+ // display size.
+ gfx::Size prefsize = GetPreferredSize();
+ size = std::max(size,
+ static_cast<int>(scroll_bar_->IsHorizontal() ?
+ prefsize.width() : prefsize.height()));
+ gfx::Rect thumb_bounds = bounds();
+ if (scroll_bar_->IsHorizontal()) {
+ thumb_bounds.set_width(size);
+ } else {
+ thumb_bounds.set_height(size);
+ }
+ SetBounds(thumb_bounds);
+ }
+
+ // Retrieves the size (width or height) of the thumb.
+ int GetSize() const {
+ if (scroll_bar_->IsHorizontal())
+ return width();
+ return height();
+ }
+
+ // Sets the position of the thumb on the x or y axis.
+ void SetPosition(int position) {
+ gfx::Rect thumb_bounds = bounds();
+ gfx::Rect track_bounds = scroll_bar_->GetTrackBounds();
+ if (scroll_bar_->IsHorizontal()) {
+ thumb_bounds.set_x(track_bounds.x() + position);
+ } else {
+ thumb_bounds.set_x(track_bounds.y() + position);
+ }
+ SetBounds(thumb_bounds);
+ }
+
+ // Gets the position of the thumb on the x or y axis.
+ int GetPosition() const {
+ gfx::Rect track_bounds = scroll_bar_->GetTrackBounds();
+ if (scroll_bar_->IsHorizontal())
+ return x() - track_bounds.x();
+ return y() - track_bounds.y();
+ }
+
+ // View overrides:
+ virtual gfx::Size GetPreferredSize() {
+ return gfx::Size(background_bitmap()->width(),
+ start_cap_bitmap()->height() +
+ end_cap_bitmap()->height() +
+ grippy_bitmap()->height());
+ }
+
+ protected:
+ // View overrides:
+ virtual void Paint(ChromeCanvas* canvas) {
+ canvas->DrawBitmapInt(*start_cap_bitmap(), 0, 0);
+ int top_cap_height = start_cap_bitmap()->height();
+ int bottom_cap_height = end_cap_bitmap()->height();
+ int thumb_body_height = height() - top_cap_height - bottom_cap_height;
+ canvas->TileImageInt(*background_bitmap(), 0, top_cap_height,
+ background_bitmap()->width(), thumb_body_height);
+ canvas->DrawBitmapInt(*end_cap_bitmap(), 0,
+ height() - bottom_cap_height);
+
+ // Paint the grippy over the track.
+ int grippy_x = (width() - grippy_bitmap()->width()) / 2;
+ int grippy_y = (thumb_body_height - grippy_bitmap()->height()) / 2;
+ canvas->DrawBitmapInt(*grippy_bitmap(), grippy_x, grippy_y);
+ }
+
+ virtual void OnMouseEntered(const MouseEvent& event) {
+ SetState(CustomButton::BS_HOT);
+ }
+
+ virtual void OnMouseExited(const MouseEvent& event) {
+ SetState(CustomButton::BS_NORMAL);
+ }
+
+ virtual bool OnMousePressed(const MouseEvent& event) {
+ mouse_offset_ = scroll_bar_->IsHorizontal() ? event.x() : event.y();
+ drag_start_position_ = GetPosition();
+ SetState(CustomButton::BS_PUSHED);
+ return true;
+ }
+
+ virtual bool OnMouseDragged(const MouseEvent& event) {
+ // If the user moves the mouse more than |kScrollThumbDragOutSnap| outside
+ // the bounds of the thumb, the scrollbar will snap the scroll back to the
+ // point it was at before the drag began.
+ if (scroll_bar_->IsHorizontal()) {
+ if ((event.y() < y() - kScrollThumbDragOutSnap) ||
+ (event.y() > (y() + height() + kScrollThumbDragOutSnap))) {
+ scroll_bar_->ScrollToThumbPosition(drag_start_position_, false);
+ return true;
+ }
+ } else {
+ if ((event.x() < x() - kScrollThumbDragOutSnap) ||
+ (event.x() > (x() + width() + kScrollThumbDragOutSnap))) {
+ scroll_bar_->ScrollToThumbPosition(drag_start_position_, false);
+ return true;
+ }
+ }
+ if (scroll_bar_->IsHorizontal()) {
+ int thumb_x = event.x() - mouse_offset_;
+ scroll_bar_->ScrollToThumbPosition(x() + thumb_x, false);
+ } else {
+ int thumb_y = event.y() - mouse_offset_;
+ scroll_bar_->ScrollToThumbPosition(y() + thumb_y, false);
+ }
+ return true;
+ }
+
+ virtual void OnMouseReleased(const MouseEvent& event,
+ bool canceled) {
+ SetState(CustomButton::BS_HOT);
+ View::OnMouseReleased(event, canceled);
+ }
+
+ private:
+ // Returns the bitmap rendered at the start of the thumb.
+ SkBitmap* start_cap_bitmap() const {
+ return scroll_bar_->images_[BitmapScrollBar::THUMB_START_CAP][state_];
+ }
+
+ // Returns the bitmap rendered at the end of the thumb.
+ SkBitmap* end_cap_bitmap() const {
+ return scroll_bar_->images_[BitmapScrollBar::THUMB_END_CAP][state_];
+ }
+
+ // Returns the bitmap that is tiled in the background of the thumb between
+ // the start and the end caps.
+ SkBitmap* background_bitmap() const {
+ return scroll_bar_->images_[BitmapScrollBar::THUMB_MIDDLE][state_];
+ }
+
+ // Returns the bitmap that is rendered in the middle of the thumb
+ // transparently over the background bitmap.
+ SkBitmap* grippy_bitmap() const {
+ return scroll_bar_->images_[BitmapScrollBar::THUMB_GRIPPY]
+ [CustomButton::BS_NORMAL];
+ }
+
+ // Update our state and schedule a repaint when the mouse moves over us.
+ void SetState(CustomButton::ButtonState state) {
+ state_ = state;
+ SchedulePaint();
+ }
+
+ // The BitmapScrollBar that owns us.
+ BitmapScrollBar* scroll_bar_;
+
+ int drag_start_position_;
+
+ // The position of the mouse on the scroll axis relative to the top of this
+ // View when the drag started.
+ int mouse_offset_;
+
+ // The current state of the thumb button.
+ CustomButton::ButtonState state_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BitmapScrollBarThumb);
+};
+
+} // anonymous namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// BitmapScrollBar, public:
+
+BitmapScrollBar::BitmapScrollBar(bool horizontal, bool show_scroll_buttons)
+ : contents_size_(0),
+ contents_scroll_offset_(0),
+ prev_button_(new AutorepeatButton(this)),
+ next_button_(new AutorepeatButton(this)),
+ thumb_(new BitmapScrollBarThumb(this)),
+ thumb_track_state_(CustomButton::BS_NORMAL),
+ last_scroll_amount_(SCROLL_NONE),
+ repeater_(NewCallback<BitmapScrollBar>(this,
+ &BitmapScrollBar::TrackClicked)),
+ context_menu_mouse_position_(0),
+ show_scroll_buttons_(show_scroll_buttons),
+ ScrollBar(horizontal) {
+ if (!show_scroll_buttons_) {
+ prev_button_->SetVisible(false);
+ next_button_->SetVisible(false);
+ }
+
+ AddChildView(prev_button_);
+ AddChildView(next_button_);
+ AddChildView(thumb_);
+
+ SetContextMenuController(this);
+ prev_button_->SetContextMenuController(this);
+ next_button_->SetContextMenuController(this);
+ thumb_->SetContextMenuController(this);
+}
+
+gfx::Rect BitmapScrollBar::GetTrackBounds() const {
+ gfx::Size prefsize = prev_button_->GetPreferredSize();
+ if (IsHorizontal()) {
+ if (!show_scroll_buttons_)
+ prefsize.set_width(0);
+ int new_width =
+ std::max(0, static_cast<int>(width() - (prefsize.width() * 2)));
+ gfx::Rect track_bounds(prefsize.width(), 0, new_width, prefsize.height());
+ return track_bounds;
+ }
+ if (!show_scroll_buttons_)
+ prefsize.set_height(0);
+ gfx::Rect track_bounds(0, prefsize.height(), prefsize.width(),
+ std::max(0, height() - (prefsize.height() * 2)));
+ return track_bounds;
+}
+
+void BitmapScrollBar::SetImage(ScrollBarPart part,
+ CustomButton::ButtonState state,
+ SkBitmap* bitmap) {
+ DCHECK(part < PART_COUNT);
+ DCHECK(state < CustomButton::BS_COUNT);
+ switch (part) {
+ case PREV_BUTTON:
+ prev_button_->SetImage(state, bitmap);
+ break;
+ case NEXT_BUTTON:
+ next_button_->SetImage(state, bitmap);
+ break;
+ case THUMB_START_CAP:
+ case THUMB_MIDDLE:
+ case THUMB_END_CAP:
+ case THUMB_GRIPPY:
+ case THUMB_TRACK:
+ images_[part][state] = bitmap;
+ break;
+ }
+}
+
+void BitmapScrollBar::ScrollByAmount(ScrollAmount amount) {
+ ScrollBarController* controller = GetController();
+ int offset = contents_scroll_offset_;
+ switch (amount) {
+ case SCROLL_START:
+ offset = GetMinPosition();
+ break;
+ case SCROLL_END:
+ offset = GetMaxPosition();
+ break;
+ case SCROLL_PREV_LINE:
+ offset -= controller->GetScrollIncrement(this, false, false);
+ offset = std::max(GetMinPosition(), offset);
+ break;
+ case SCROLL_NEXT_LINE:
+ offset += controller->GetScrollIncrement(this, false, true);
+ offset = std::min(GetMaxPosition(), offset);
+ break;
+ case SCROLL_PREV_PAGE:
+ offset -= controller->GetScrollIncrement(this, true, false);
+ offset = std::max(GetMinPosition(), offset);
+ break;
+ case SCROLL_NEXT_PAGE:
+ offset += controller->GetScrollIncrement(this, true, true);
+ offset = std::min(GetMaxPosition(), offset);
+ break;
+ }
+ contents_scroll_offset_ = offset;
+ ScrollContentsToOffset();
+}
+
+void BitmapScrollBar::ScrollToThumbPosition(int thumb_position,
+ bool scroll_to_middle) {
+ contents_scroll_offset_ =
+ CalculateContentsOffset(thumb_position, scroll_to_middle);
+ if (contents_scroll_offset_ < GetMinPosition()) {
+ contents_scroll_offset_ = GetMinPosition();
+ } else if (contents_scroll_offset_ > GetMaxPosition()) {
+ contents_scroll_offset_ = GetMaxPosition();
+ }
+ ScrollContentsToOffset();
+ SchedulePaint();
+}
+
+void BitmapScrollBar::ScrollByContentsOffset(int contents_offset) {
+ contents_scroll_offset_ -= contents_offset;
+ if (contents_scroll_offset_ < GetMinPosition()) {
+ contents_scroll_offset_ = GetMinPosition();
+ } else if (contents_scroll_offset_ > GetMaxPosition()) {
+ contents_scroll_offset_ = GetMaxPosition();
+ }
+ ScrollContentsToOffset();
+}
+
+void BitmapScrollBar::TrackClicked() {
+ if (last_scroll_amount_ != SCROLL_NONE)
+ ScrollByAmount(last_scroll_amount_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BitmapScrollBar, View implementation:
+
+gfx::Size BitmapScrollBar::GetPreferredSize() {
+ // In this case, we're returning the desired width of the scrollbar and its
+ // minimum allowable height.
+ gfx::Size button_prefsize = prev_button_->GetPreferredSize();
+ return gfx::Size(button_prefsize.width(), button_prefsize.height() * 2);
+}
+
+void BitmapScrollBar::Paint(ChromeCanvas* canvas) {
+ // Paint the track.
+ gfx::Rect track_bounds = GetTrackBounds();
+ canvas->TileImageInt(*images_[THUMB_TRACK][thumb_track_state_],
+ track_bounds.x(), track_bounds.y(),
+ track_bounds.width(), track_bounds.height());
+}
+
+void BitmapScrollBar::Layout() {
+ // Size and place the two scroll buttons.
+ if (show_scroll_buttons_) {
+ gfx::Size prefsize = prev_button_->GetPreferredSize();
+ prev_button_->SetBounds(0, 0, prefsize.width(), prefsize.height());
+ prefsize = next_button_->GetPreferredSize();
+ if (IsHorizontal()) {
+ next_button_->SetBounds(width() - prefsize.width(), 0, prefsize.width(),
+ prefsize.height());
+ } else {
+ next_button_->SetBounds(0, height() - prefsize.height(), prefsize.width(),
+ prefsize.height());
+ }
+ } else {
+ prev_button_->SetBounds(0, 0, 0, 0);
+ next_button_->SetBounds(0, 0, 0, 0);
+ }
+
+ // Size and place the thumb
+ gfx::Size thumb_prefsize = thumb_->GetPreferredSize();
+ gfx::Rect track_bounds = GetTrackBounds();
+
+ // Preserve the height/width of the thumb (depending on orientation) as set
+ // by the last call to |Update|, but coerce the width/height to be the
+ // appropriate value for the bitmaps provided.
+ if (IsHorizontal()) {
+ thumb_->SetBounds(thumb_->x(), thumb_->y(), thumb_->width(),
+ thumb_prefsize.height());
+ } else {
+ thumb_->SetBounds(thumb_->x(), thumb_->y(), thumb_prefsize.width(),
+ thumb_->height());
+ }
+
+ // Hide the thumb if the track isn't tall enough to display even a tiny
+ // thumb. The user can only use the mousewheel, scroll buttons or keyboard
+ // in this scenario.
+ if ((IsHorizontal() && (track_bounds.width() < thumb_prefsize.width()) ||
+ (!IsHorizontal() && (track_bounds.height() < thumb_prefsize.height())))) {
+ thumb_->SetVisible(false);
+ } else if (!thumb_->IsVisible()) {
+ thumb_->SetVisible(true);
+ }
+}
+
+bool BitmapScrollBar::OnMousePressed(const MouseEvent& event) {
+ if (event.IsOnlyLeftMouseButton()) {
+ SetThumbTrackState(CustomButton::BS_PUSHED);
+ gfx::Rect thumb_bounds = thumb_->bounds();
+ if (IsHorizontal()) {
+ if (event.x() < thumb_bounds.x()) {
+ last_scroll_amount_ = SCROLL_PREV_PAGE;
+ } else if (event.x() > thumb_bounds.right()) {
+ last_scroll_amount_ = SCROLL_NEXT_PAGE;
+ }
+ } else {
+ if (event.y() < thumb_bounds.y()) {
+ last_scroll_amount_ = SCROLL_PREV_PAGE;
+ } else if (event.y() > thumb_bounds.bottom()) {
+ last_scroll_amount_ = SCROLL_NEXT_PAGE;
+ }
+ }
+ TrackClicked();
+ repeater_.Start();
+ }
+ return true;
+}
+
+void BitmapScrollBar::OnMouseReleased(const MouseEvent& event, bool canceled) {
+ SetThumbTrackState(CustomButton::BS_NORMAL);
+ repeater_.Stop();
+ View::OnMouseReleased(event, canceled);
+}
+
+bool BitmapScrollBar::OnMouseWheel(const MouseWheelEvent& event) {
+ ScrollByContentsOffset(event.GetOffset());
+ return true;
+}
+
+bool BitmapScrollBar::OnKeyPressed(const KeyEvent& event) {
+ ScrollAmount amount = SCROLL_NONE;
+ switch(event.GetCharacter()) {
+ case VK_UP:
+ if (!IsHorizontal())
+ amount = SCROLL_PREV_LINE;
+ break;
+ case VK_DOWN:
+ if (!IsHorizontal())
+ amount = SCROLL_NEXT_LINE;
+ break;
+ case VK_LEFT:
+ if (IsHorizontal())
+ amount = SCROLL_PREV_LINE;
+ break;
+ case VK_RIGHT:
+ if (IsHorizontal())
+ amount = SCROLL_NEXT_LINE;
+ break;
+ case VK_PRIOR:
+ amount = SCROLL_PREV_PAGE;
+ break;
+ case VK_NEXT:
+ amount = SCROLL_NEXT_PAGE;
+ break;
+ case VK_HOME:
+ amount = SCROLL_START;
+ break;
+ case VK_END:
+ amount = SCROLL_END;
+ break;
+ }
+ if (amount != SCROLL_NONE) {
+ ScrollByAmount(amount);
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BitmapScrollBar, ContextMenuController implementation:
+
+enum ScrollBarContextMenuCommands {
+ ScrollBarContextMenuCommand_ScrollHere = 1,
+ ScrollBarContextMenuCommand_ScrollStart,
+ ScrollBarContextMenuCommand_ScrollEnd,
+ ScrollBarContextMenuCommand_ScrollPageUp,
+ ScrollBarContextMenuCommand_ScrollPageDown,
+ ScrollBarContextMenuCommand_ScrollPrev,
+ ScrollBarContextMenuCommand_ScrollNext
+};
+
+void BitmapScrollBar::ShowContextMenu(View* source,
+ int x,
+ int y,
+ bool is_mouse_gesture) {
+ Widget* widget = GetWidget();
+ gfx::Rect widget_bounds;
+ widget->GetBounds(&widget_bounds, true);
+ gfx::Point temp_pt(x - widget_bounds.x(), y - widget_bounds.y());
+ View::ConvertPointFromWidget(this, &temp_pt);
+ context_menu_mouse_position_ = IsHorizontal() ? temp_pt.x() : temp_pt.y();
+
+ Menu menu(this, Menu::TOPLEFT, GetWidget()->GetNativeView());
+ menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollHere);
+ menu.AppendSeparator();
+ menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollStart);
+ menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollEnd);
+ menu.AppendSeparator();
+ menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPageUp);
+ menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPageDown);
+ menu.AppendSeparator();
+ menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPrev);
+ menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollNext);
+ menu.RunMenuAt(x, y);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BitmapScrollBar, Menu::Delegate implementation:
+
+std::wstring BitmapScrollBar::GetLabel(int id) const {
+ switch (id) {
+ case ScrollBarContextMenuCommand_ScrollHere:
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLHERE);
+ case ScrollBarContextMenuCommand_ScrollStart:
+ if (IsHorizontal())
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLLEFTEDGE);
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLHOME);
+ case ScrollBarContextMenuCommand_ScrollEnd:
+ if (IsHorizontal())
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLRIGHTEDGE);
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLEND);
+ case ScrollBarContextMenuCommand_ScrollPageUp:
+ if (IsHorizontal())
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLPAGEUP);
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLPAGEUP);
+ case ScrollBarContextMenuCommand_ScrollPageDown:
+ if (IsHorizontal())
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLPAGEDOWN);
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLPAGEDOWN);
+ case ScrollBarContextMenuCommand_ScrollPrev:
+ if (IsHorizontal())
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLLEFT);
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLUP);
+ case ScrollBarContextMenuCommand_ScrollNext:
+ if (IsHorizontal())
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLRIGHT);
+ return l10n_util::GetString(IDS_SCROLLBAR_CXMENU_SCROLLDOWN);
+ }
+ NOTREACHED() << "Invalid BitmapScrollBar Context Menu command!";
+ return L"";
+}
+
+bool BitmapScrollBar::IsCommandEnabled(int id) const {
+ switch (id) {
+ case ScrollBarContextMenuCommand_ScrollPageUp:
+ case ScrollBarContextMenuCommand_ScrollPageDown:
+ return !IsHorizontal();
+ }
+ return true;
+}
+
+void BitmapScrollBar::ExecuteCommand(int id) {
+ switch (id) {
+ case ScrollBarContextMenuCommand_ScrollHere:
+ ScrollToThumbPosition(context_menu_mouse_position_, true);
+ break;
+ case ScrollBarContextMenuCommand_ScrollStart:
+ ScrollByAmount(SCROLL_START);
+ break;
+ case ScrollBarContextMenuCommand_ScrollEnd:
+ ScrollByAmount(SCROLL_END);
+ break;
+ case ScrollBarContextMenuCommand_ScrollPageUp:
+ ScrollByAmount(SCROLL_PREV_PAGE);
+ break;
+ case ScrollBarContextMenuCommand_ScrollPageDown:
+ ScrollByAmount(SCROLL_NEXT_PAGE);
+ break;
+ case ScrollBarContextMenuCommand_ScrollPrev:
+ ScrollByAmount(SCROLL_PREV_LINE);
+ break;
+ case ScrollBarContextMenuCommand_ScrollNext:
+ ScrollByAmount(SCROLL_NEXT_LINE);
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BitmapScrollBar, ButtonListener implementation:
+
+void BitmapScrollBar::ButtonPressed(Button* sender) {
+ if (sender == prev_button_) {
+ ScrollByAmount(SCROLL_PREV_LINE);
+ } else if (sender == next_button_) {
+ ScrollByAmount(SCROLL_NEXT_LINE);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BitmapScrollBar, ScrollBar implementation:
+
+void BitmapScrollBar::Update(int viewport_size, int content_size,
+ int contents_scroll_offset) {
+ ScrollBar::Update(viewport_size, content_size, contents_scroll_offset);
+
+ // Make sure contents_size is always > 0 to avoid divide by zero errors in
+ // calculations throughout this code.
+ contents_size_ = std::max(1, content_size);
+
+ if (content_size < 0)
+ content_size = 0;
+ if (contents_scroll_offset < 0)
+ contents_scroll_offset = 0;
+ if (contents_scroll_offset > content_size)
+ contents_scroll_offset = content_size;
+
+ // Thumb Height and Thumb Pos.
+ // The height of the thumb is the ratio of the Viewport height to the
+ // content size multiplied by the height of the thumb track.
+ double ratio = static_cast<double>(viewport_size) / contents_size_;
+ int thumb_size = static_cast<int>(ratio * GetTrackSize());
+ thumb_->SetSize(thumb_size);
+
+ int thumb_position = CalculateThumbPosition(contents_scroll_offset);
+ thumb_->SetPosition(thumb_position);
+}
+
+int BitmapScrollBar::GetLayoutSize() const {
+ gfx::Size prefsize = prev_button_->GetPreferredSize();
+ return IsHorizontal() ? prefsize.height() : prefsize.width();
+}
+
+int BitmapScrollBar::GetPosition() const {
+ return thumb_->GetPosition();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BitmapScrollBar, private:
+
+void BitmapScrollBar::ScrollContentsToOffset() {
+ GetController()->ScrollToPosition(this, contents_scroll_offset_);
+ thumb_->SetPosition(CalculateThumbPosition(contents_scroll_offset_));
+}
+
+int BitmapScrollBar::GetTrackSize() const {
+ gfx::Rect track_bounds = GetTrackBounds();
+ return IsHorizontal() ? track_bounds.width() : track_bounds.height();
+}
+
+int BitmapScrollBar::CalculateThumbPosition(int contents_scroll_offset) const {
+ return (contents_scroll_offset * GetTrackSize()) / contents_size_;
+}
+
+int BitmapScrollBar::CalculateContentsOffset(int thumb_position,
+ bool scroll_to_middle) const {
+ if (scroll_to_middle)
+ thumb_position = thumb_position - (thumb_->GetSize() / 2);
+ return (thumb_position * contents_size_) / GetTrackSize();
+}
+
+void BitmapScrollBar::SetThumbTrackState(CustomButton::ButtonState state) {
+ thumb_track_state_ = state;
+ SchedulePaint();
+}
+
+} // namespace views
diff --git a/views/controls/scrollbar/bitmap_scroll_bar.h b/views/controls/scrollbar/bitmap_scroll_bar.h
new file mode 100644
index 0000000..45e3535
--- /dev/null
+++ b/views/controls/scrollbar/bitmap_scroll_bar.h
@@ -0,0 +1,192 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef VIEWS_CONTROLS_SCROLLBAR_BITMAP_SCROLL_BAR_H_
+#define VIEWS_CONTROLS_SCROLLBAR_BITMAP_SCROLL_BAR_H_
+
+#include "views/controls/button/image_button.h"
+#include "views/controls/menu/menu.h"
+#include "views/controls/scrollbar/scroll_bar.h"
+#include "views/repeat_controller.h"
+
+namespace views {
+
+namespace {
+class BitmapScrollBarThumb;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// BitmapScrollBar
+//
+// A ScrollBar subclass that implements a scroll bar rendered using bitmaps
+// that the user provides. There are bitmaps for the up and down buttons, as
+// well as for the thumb and track. This is intended for creating UIs that
+// have customized, non-native appearances, like floating HUDs etc.
+//
+// Maybe TODO(beng): (Cleanup) If we need to, we may want to factor rendering
+// out of this altogether and have the user supply
+// Background impls for each component, and just use those
+// to render, so that for example we get native theme
+// rendering.
+//
+///////////////////////////////////////////////////////////////////////////////
+class BitmapScrollBar : public ScrollBar,
+ public ButtonListener,
+ public ContextMenuController,
+ public Menu::Delegate {
+ public:
+ BitmapScrollBar(bool horizontal, bool show_scroll_buttons);
+ virtual ~BitmapScrollBar() { }
+
+ // Get the bounds of the "track" area that the thumb is free to slide within.
+ gfx::Rect GetTrackBounds() const;
+
+ // A list of parts that the user may supply bitmaps for.
+ enum ScrollBarPart {
+ // The button used to represent scrolling up/left by 1 line.
+ PREV_BUTTON = 0,
+ // The button used to represent scrolling down/right by 1 line.
+ // IMPORTANT: The code assumes the prev and next
+ // buttons have equal width and equal height.
+ NEXT_BUTTON,
+ // The top/left segment of the thumb on the scrollbar.
+ THUMB_START_CAP,
+ // The tiled background image of the thumb.
+ THUMB_MIDDLE,
+ // The bottom/right segment of the thumb on the scrollbar.
+ THUMB_END_CAP,
+ // The grippy that is rendered in the center of the thumb.
+ THUMB_GRIPPY,
+ // The tiled background image of the thumb track.
+ THUMB_TRACK,
+ PART_COUNT
+ };
+
+ // Sets the bitmap to be rendered for the specified part and state.
+ void SetImage(ScrollBarPart part,
+ CustomButton::ButtonState state,
+ SkBitmap* bitmap);
+
+ // An enumeration of different amounts of incremental scroll, representing
+ // events sent from different parts of the UI/keyboard.
+ enum ScrollAmount {
+ SCROLL_NONE = 0,
+ SCROLL_START,
+ SCROLL_END,
+ SCROLL_PREV_LINE,
+ SCROLL_NEXT_LINE,
+ SCROLL_PREV_PAGE,
+ SCROLL_NEXT_PAGE,
+ };
+
+ // Scroll the contents by the specified type (see ScrollAmount above).
+ void ScrollByAmount(ScrollAmount amount);
+
+ // Scroll the contents to the appropriate position given the supplied
+ // position of the thumb (thumb track coordinates). If |scroll_to_middle| is
+ // true, then the conversion assumes |thumb_position| is in the middle of the
+ // thumb rather than the top.
+ void ScrollToThumbPosition(int thumb_position, bool scroll_to_middle);
+
+ // Scroll the contents by the specified offset (contents coordinates).
+ void ScrollByContentsOffset(int contents_offset);
+
+ // View overrides:
+ virtual gfx::Size GetPreferredSize();
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void Layout();
+ virtual bool OnMousePressed(const MouseEvent& event);
+ virtual void OnMouseReleased(const MouseEvent& event, bool canceled);
+ virtual bool OnMouseWheel(const MouseWheelEvent& event);
+ virtual bool OnKeyPressed(const KeyEvent& event);
+
+ // BaseButton::ButtonListener overrides:
+ virtual void ButtonPressed(Button* sender);
+
+ // ScrollBar overrides:
+ virtual void Update(int viewport_size,
+ int content_size,
+ int contents_scroll_offset);
+ virtual int GetLayoutSize() const;
+ virtual int GetPosition() const;
+
+ // ContextMenuController overrides.
+ virtual void ShowContextMenu(View* source,
+ int x,
+ int y,
+ bool is_mouse_gesture);
+
+ // Menu::Delegate overrides:
+ virtual std::wstring GetLabel(int id) const;
+ virtual bool IsCommandEnabled(int id) const;
+ virtual void ExecuteCommand(int id);
+
+ private:
+ // Called when the mouse is pressed down in the track area.
+ void TrackClicked();
+
+ // Responsible for scrolling the contents and also updating the UI to the
+ // current value of the Scroll Offset.
+ void ScrollContentsToOffset();
+
+ // Returns the size (width or height) of the track area of the ScrollBar.
+ int GetTrackSize() const;
+
+ // Calculate the position of the thumb within the track based on the
+ // specified scroll offset of the contents.
+ int CalculateThumbPosition(int contents_scroll_offset) const;
+
+ // Calculates the current value of the contents offset (contents coordinates)
+ // based on the current thumb position (thumb track coordinates). See
+ // |ScrollToThumbPosition| for an explanation of |scroll_to_middle|.
+ int CalculateContentsOffset(int thumb_position,
+ bool scroll_to_middle) const;
+
+ // Called when the state of the thumb track changes (e.g. by the user
+ // pressing the mouse button down in it).
+ void SetThumbTrackState(CustomButton::ButtonState state);
+
+ // The thumb needs to be able to access the part images.
+ friend BitmapScrollBarThumb;
+ SkBitmap* images_[PART_COUNT][CustomButton::BS_COUNT];
+
+ // The size of the scrolled contents, in pixels.
+ int contents_size_;
+
+ // The current amount the contents is offset by in the viewport.
+ int contents_scroll_offset_;
+
+ // Up/Down/Left/Right buttons and the Thumb.
+ ImageButton* prev_button_;
+ ImageButton* next_button_;
+ BitmapScrollBarThumb* thumb_;
+
+ // The state of the scrollbar track. Typically, the track will highlight when
+ // the user presses the mouse on them (during page scrolling).
+ CustomButton::ButtonState thumb_track_state_;
+
+ // The last amount of incremental scroll that this scrollbar performed. This
+ // is accessed by the callbacks for the auto-repeat up/down buttons to know
+ // what direction to repeatedly scroll in.
+ ScrollAmount last_scroll_amount_;
+
+ // An instance of a RepeatController which scrolls the scrollbar continuously
+ // as the user presses the mouse button down on the up/down buttons or the
+ // track.
+ RepeatController repeater_;
+
+ // The position of the mouse within the scroll bar when the context menu
+ // was invoked.
+ int context_menu_mouse_position_;
+
+ // True if the scroll buttons at each end of the scroll bar should be shown.
+ bool show_scroll_buttons_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BitmapScrollBar);
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_CONTROLS_SCROLLBAR_BITMAP_SCROLL_BAR_H_
diff --git a/views/controls/scrollbar/native_scroll_bar.cc b/views/controls/scrollbar/native_scroll_bar.cc
new file mode 100644
index 0000000..52ddd91
--- /dev/null
+++ b/views/controls/scrollbar/native_scroll_bar.cc
@@ -0,0 +1,357 @@
+// Copyright (c) 2006-2008 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 "views/controls/scrollbar/native_scroll_bar.h"
+
+#include <atlbase.h>
+#include <atlapp.h>
+#include <atlwin.h>
+#include <atlcrack.h>
+#include <atlframe.h>
+#include <atlmisc.h>
+#include <string>
+
+#include "base/message_loop.h"
+#include "views/controls/hwnd_view.h"
+#include "views/widget/widget.h"
+
+namespace views {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// ScrollBarContainer
+//
+// Since windows scrollbar only send notifications to their parent hwnd, we
+// use instance of this class to wrap native scrollbars.
+//
+/////////////////////////////////////////////////////////////////////////////
+class ScrollBarContainer : public CWindowImpl<ScrollBarContainer,
+ CWindow,
+ CWinTraits<WS_CHILD>> {
+ public:
+ ScrollBarContainer(ScrollBar* parent) : parent_(parent),
+ scrollbar_(NULL) {
+ Create(parent->GetWidget()->GetNativeView());
+ ::ShowWindow(m_hWnd, SW_SHOW);
+ }
+
+ virtual ~ScrollBarContainer() {
+ }
+
+ DECLARE_FRAME_WND_CLASS(L"ChromeViewsScrollBarContainer", NULL);
+ BEGIN_MSG_MAP(ScrollBarContainer);
+ MSG_WM_CREATE(OnCreate);
+ MSG_WM_ERASEBKGND(OnEraseBkgnd);
+ MSG_WM_PAINT(OnPaint);
+ MSG_WM_SIZE(OnSize);
+ MSG_WM_HSCROLL(OnHorizScroll);
+ MSG_WM_VSCROLL(OnVertScroll);
+ END_MSG_MAP();
+
+ HWND GetScrollBarHWND() {
+ return scrollbar_;
+ }
+
+ // Invoked when the scrollwheel is used
+ void ScrollWithOffset(int o) {
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ ::GetScrollInfo(scrollbar_, SB_CTL, &si);
+ int pos = si.nPos - o;
+
+ if (pos < parent_->GetMinPosition())
+ pos = parent_->GetMinPosition();
+ else if (pos > parent_->GetMaxPosition())
+ pos = parent_->GetMaxPosition();
+
+ ScrollBarController* sbc = parent_->GetController();
+ sbc->ScrollToPosition(parent_, pos);
+
+ si.nPos = pos;
+ si.fMask = SIF_POS;
+ ::SetScrollInfo(scrollbar_, SB_CTL, &si, TRUE);
+ }
+
+ private:
+
+ LRESULT OnCreate(LPCREATESTRUCT create_struct) {
+ scrollbar_ = CreateWindow(L"SCROLLBAR",
+ L"",
+ WS_CHILD | (parent_->IsHorizontal() ?
+ SBS_HORZ : SBS_VERT),
+ 0,
+ 0,
+ parent_->width(),
+ parent_->height(),
+ m_hWnd,
+ NULL,
+ NULL,
+ NULL);
+ ::ShowWindow(scrollbar_, SW_SHOW);
+ return 1;
+ }
+
+ LRESULT OnEraseBkgnd(HDC dc) {
+ return 1;
+ }
+
+ void OnPaint(HDC ignore) {
+ PAINTSTRUCT ps;
+ HDC dc = ::BeginPaint(*this, &ps);
+ ::EndPaint(*this, &ps);
+ }
+
+ void OnSize(int type, const CSize& sz) {
+ ::SetWindowPos(scrollbar_,
+ 0,
+ 0,
+ 0,
+ sz.cx,
+ sz.cy,
+ SWP_DEFERERASE |
+ SWP_NOACTIVATE |
+ SWP_NOCOPYBITS |
+ SWP_NOOWNERZORDER |
+ SWP_NOSENDCHANGING |
+ SWP_NOZORDER);
+ }
+
+ void OnScroll(int code, HWND source, bool is_horizontal) {
+ int pos;
+
+ if (code == SB_ENDSCROLL) {
+ return;
+ }
+
+ // If we receive an event from the scrollbar, make the view
+ // component focused so we actually get mousewheel events.
+ if (source != NULL) {
+ Widget* widget = parent_->GetWidget();
+ if (widget && widget->GetNativeView() != GetFocus()) {
+ parent_->RequestFocus();
+ }
+ }
+
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS | SIF_TRACKPOS;
+ ::GetScrollInfo(scrollbar_, SB_CTL, &si);
+ pos = si.nPos;
+
+ ScrollBarController* sbc = parent_->GetController();
+
+ switch (code) {
+ case SB_BOTTOM: // case SB_RIGHT:
+ pos = parent_->GetMaxPosition();
+ break;
+ case SB_TOP: // case SB_LEFT:
+ pos = parent_->GetMinPosition();
+ break;
+ case SB_LINEDOWN: // case SB_LINERIGHT:
+ pos += sbc->GetScrollIncrement(parent_, false, true);
+ pos = std::min(parent_->GetMaxPosition(), pos);
+ break;
+ case SB_LINEUP: // case SB_LINELEFT:
+ pos -= sbc->GetScrollIncrement(parent_, false, false);
+ pos = std::max(parent_->GetMinPosition(), pos);
+ break;
+ case SB_PAGEDOWN: // case SB_PAGERIGHT:
+ pos += sbc->GetScrollIncrement(parent_, true, true);
+ pos = std::min(parent_->GetMaxPosition(), pos);
+ break;
+ case SB_PAGEUP: // case SB_PAGELEFT:
+ pos -= sbc->GetScrollIncrement(parent_, true, false);
+ pos = std::max(parent_->GetMinPosition(), pos);
+ break;
+ case SB_THUMBPOSITION:
+ case SB_THUMBTRACK:
+ pos = si.nTrackPos;
+ if (pos < parent_->GetMinPosition())
+ pos = parent_->GetMinPosition();
+ else if (pos > parent_->GetMaxPosition())
+ pos = parent_->GetMaxPosition();
+ break;
+ default:
+ break;
+ }
+
+ sbc->ScrollToPosition(parent_, pos);
+
+ si.nPos = pos;
+ si.fMask = SIF_POS;
+ ::SetScrollInfo(scrollbar_, SB_CTL, &si, TRUE);
+
+ // Note: the system scrollbar modal loop doesn't give a chance
+ // to our message_loop so we need to call DidProcessMessage()
+ // manually.
+ //
+ // Sadly, we don't know what message has been processed. We may
+ // want to remove the message from DidProcessMessage()
+ MSG dummy;
+ dummy.hwnd = NULL;
+ dummy.message = 0;
+ MessageLoopForUI::current()->DidProcessMessage(dummy);
+ }
+
+ // note: always ignore 2nd param as it is 16 bits
+ void OnHorizScroll(int n_sb_code, int ignore, HWND source) {
+ OnScroll(n_sb_code, source, true);
+ }
+
+ // note: always ignore 2nd param as it is 16 bits
+ void OnVertScroll(int n_sb_code, int ignore, HWND source) {
+ OnScroll(n_sb_code, source, false);
+ }
+
+
+
+ ScrollBar* parent_;
+ HWND scrollbar_;
+};
+
+NativeScrollBar::NativeScrollBar(bool is_horiz)
+ : sb_view_(NULL),
+ sb_container_(NULL),
+ ScrollBar(is_horiz) {
+}
+
+NativeScrollBar::~NativeScrollBar() {
+ if (sb_container_) {
+ // We always destroy the scrollbar container explicitly to cover all
+ // cases including when the container is no longer connected to a
+ // widget tree.
+ ::DestroyWindow(*sb_container_);
+ delete sb_container_;
+ }
+}
+
+void NativeScrollBar::ViewHierarchyChanged(bool is_add, View *parent,
+ View *child) {
+ Widget* widget;
+ if (is_add && (widget = GetWidget()) && !sb_view_) {
+ sb_view_ = new HWNDView();
+ AddChildView(sb_view_);
+ sb_container_ = new ScrollBarContainer(this);
+ sb_view_->Attach(*sb_container_);
+ Layout();
+ }
+}
+
+void NativeScrollBar::Layout() {
+ if (sb_view_)
+ sb_view_->SetBounds(GetLocalBounds(true));
+}
+
+gfx::Size NativeScrollBar::GetPreferredSize() {
+ if (IsHorizontal())
+ return gfx::Size(0, GetLayoutSize());
+ return gfx::Size(GetLayoutSize(), 0);
+}
+
+void NativeScrollBar::Update(int viewport_size,
+ int content_size,
+ int current_pos) {
+ ScrollBar::Update(viewport_size, content_size, current_pos);
+ if (!sb_container_)
+ return;
+
+ if (content_size < 0)
+ content_size = 0;
+
+ if (current_pos < 0)
+ current_pos = 0;
+
+ if (current_pos > content_size)
+ current_pos = content_size;
+
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_DISABLENOSCROLL | SIF_POS | SIF_RANGE | SIF_PAGE;
+ si.nMin = 0;
+ si.nMax = content_size;
+ si.nPos = current_pos;
+ si.nPage = viewport_size;
+ ::SetScrollInfo(sb_container_->GetScrollBarHWND(),
+ SB_CTL,
+ &si,
+ TRUE);
+}
+
+int NativeScrollBar::GetLayoutSize() const {
+ return ::GetSystemMetrics(IsHorizontal() ? SM_CYHSCROLL : SM_CYVSCROLL);
+}
+
+int NativeScrollBar::GetPosition() const {
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ GetScrollInfo(sb_container_->GetScrollBarHWND(), SB_CTL, &si);
+ return si.nPos;
+}
+
+bool NativeScrollBar::OnMouseWheel(const MouseWheelEvent& e) {
+ if (!sb_container_) {
+ return false;
+ }
+
+ sb_container_->ScrollWithOffset(e.GetOffset());
+ return true;
+}
+
+bool NativeScrollBar::OnKeyPressed(const KeyEvent& event) {
+ if (!sb_container_) {
+ return false;
+ }
+ int code = -1;
+ switch(event.GetCharacter()) {
+ case VK_UP:
+ if (!IsHorizontal())
+ code = SB_LINEUP;
+ break;
+ case VK_PRIOR:
+ code = SB_PAGEUP;
+ break;
+ case VK_NEXT:
+ code = SB_PAGEDOWN;
+ break;
+ case VK_DOWN:
+ if (!IsHorizontal())
+ code = SB_LINEDOWN;
+ break;
+ case VK_HOME:
+ code = SB_TOP;
+ break;
+ case VK_END:
+ code = SB_BOTTOM;
+ break;
+ case VK_LEFT:
+ if (IsHorizontal())
+ code = SB_LINELEFT;
+ break;
+ case VK_RIGHT:
+ if (IsHorizontal())
+ code = SB_LINERIGHT;
+ break;
+ }
+ if (code != -1) {
+ ::SendMessage(*sb_container_,
+ IsHorizontal() ? WM_HSCROLL : WM_VSCROLL,
+ MAKELONG(static_cast<WORD>(code), 0), 0L);
+ return true;
+ }
+ return false;
+}
+
+//static
+int NativeScrollBar::GetHorizontalScrollBarHeight() {
+ return ::GetSystemMetrics(SM_CYHSCROLL);
+}
+
+//static
+int NativeScrollBar::GetVerticalScrollBarWidth() {
+ return ::GetSystemMetrics(SM_CXVSCROLL);
+}
+
+} // namespace views
diff --git a/views/controls/scrollbar/native_scroll_bar.h b/views/controls/scrollbar/native_scroll_bar.h
new file mode 100644
index 0000000..2747bce
--- /dev/null
+++ b/views/controls/scrollbar/native_scroll_bar.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef VIEWS_CONTROLS_SCROLLBAR_NATIVE_SCROLLBAR_H_
+#define VIEWS_CONTROLS_SCROLLBAR_NATIVE_SCROLLBAR_H_
+
+#include "build/build_config.h"
+
+#include "views/controls/scrollbar/scroll_bar.h"
+
+namespace views {
+
+class HWNDView;
+class ScrollBarContainer;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// NativeScrollBar
+//
+// A View subclass that wraps a Native Windows scrollbar control.
+//
+// A scrollbar is either horizontal or vertical.
+//
+/////////////////////////////////////////////////////////////////////////////
+class NativeScrollBar : public ScrollBar {
+ public:
+
+ // Create new scrollbar, either horizontal or vertical
+ explicit NativeScrollBar(bool is_horiz);
+ virtual ~NativeScrollBar();
+
+ // Overridden for layout purpose
+ virtual void Layout();
+ virtual gfx::Size GetPreferredSize();
+
+ // Overridden for keyboard UI purpose
+ virtual bool OnKeyPressed(const KeyEvent& event);
+ virtual bool OnMouseWheel(const MouseWheelEvent& e);
+
+ virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child);
+
+ // Overridden from ScrollBar
+ virtual void Update(int viewport_size, int content_size, int current_pos);
+ virtual int GetLayoutSize() const;
+ virtual int GetPosition() const;
+
+ // Return the system sizes
+ static int GetHorizontalScrollBarHeight();
+ static int GetVerticalScrollBarWidth();
+
+ private:
+#if defined(OS_WIN)
+ // The sb_view_ takes care of keeping sb_container in sync with the
+ // view hierarchy
+ HWNDView* sb_view_;
+#endif // defined(OS_WIN)
+
+ // sb_container_ is a custom hwnd that we use to wrap the real
+ // windows scrollbar. We need to do this to get the scroll events
+ // without having to do anything special in the high level hwnd.
+ ScrollBarContainer* sb_container_;
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_CONTROLS_SCROLLBAR_NATIVE_SCROLLBAR_H_
diff --git a/views/controls/scrollbar/scroll_bar.cc b/views/controls/scrollbar/scroll_bar.cc
new file mode 100644
index 0000000..a475c44
--- /dev/null
+++ b/views/controls/scrollbar/scroll_bar.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 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 "views/controls/scrollbar/scroll_bar.h"
+
+namespace views {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// ScrollBar implementation
+//
+/////////////////////////////////////////////////////////////////////////////
+
+ScrollBar::ScrollBar(bool is_horiz) : is_horiz_(is_horiz),
+ controller_(NULL),
+ max_pos_(0) {
+}
+
+ScrollBar::~ScrollBar() {
+}
+
+bool ScrollBar::IsHorizontal() const {
+ return is_horiz_;
+}
+
+void ScrollBar::SetController(ScrollBarController* controller) {
+ controller_ = controller;
+}
+
+ScrollBarController* ScrollBar::GetController() const {
+ return controller_;
+}
+
+void ScrollBar::Update(int viewport_size, int content_size, int current_pos) {
+ max_pos_ = std::max(0, content_size - viewport_size);
+}
+
+int ScrollBar::GetMaxPosition() const {
+ return max_pos_;
+}
+
+int ScrollBar::GetMinPosition() const {
+ return 0;
+}
+
+} // namespace views
diff --git a/views/controls/scrollbar/scroll_bar.h b/views/controls/scrollbar/scroll_bar.h
new file mode 100644
index 0000000..36a9d2e
--- /dev/null
+++ b/views/controls/scrollbar/scroll_bar.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef VIEWS_CONTROLS_SCROLLBAR_SCROLLBAR_H_
+#define VIEWS_CONTROLS_SCROLLBAR_SCROLLBAR_H_
+
+#include "views/view.h"
+#include "views/event.h"
+
+namespace views {
+
+class ScrollBar;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// ScrollBarController
+//
+// ScrollBarController defines the method that should be implemented to
+// receive notification from a scrollbar
+//
+/////////////////////////////////////////////////////////////////////////////
+class ScrollBarController {
+ public:
+
+ // Invoked by the scrollbar when the scrolling position changes
+ // This method typically implements the actual scrolling.
+ //
+ // The provided position is expressed in pixels. It is the new X or Y
+ // position which is in the GetMinPosition() / GetMaxPosition range.
+ virtual void ScrollToPosition(ScrollBar* source, int position) = 0;
+
+ // Returns the amount to scroll. The amount to scroll may be requested in
+ // two different amounts. If is_page is true the 'page scroll' amount is
+ // requested. The page scroll amount typically corresponds to the
+ // visual size of the view. If is_page is false, the 'line scroll' amount
+ // is being requested. The line scroll amount typically corresponds to the
+ // size of one row/column.
+ //
+ // The return value should always be positive. A value <= 0 results in
+ // scrolling by a fixed amount.
+ virtual int GetScrollIncrement(ScrollBar* source,
+ bool is_page,
+ bool is_positive) = 0;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// ScrollBar
+//
+// A View subclass to wrap to implement a ScrollBar. Our current windows
+// version simply wraps a native windows scrollbar.
+//
+// A scrollbar is either horizontal or vertical
+//
+/////////////////////////////////////////////////////////////////////////////
+class ScrollBar : public View {
+ public:
+ virtual ~ScrollBar();
+
+ // Return whether this scrollbar is horizontal
+ bool IsHorizontal() const;
+
+ // Set / Get the controller
+ void SetController(ScrollBarController* controller);
+ ScrollBarController* GetController() const;
+
+ // Update the scrollbar appearance given a viewport size, content size and
+ // current position
+ virtual void Update(int viewport_size, int content_size, int current_pos);
+
+ // Return the max and min positions
+ int GetMaxPosition() const;
+ int GetMinPosition() const;
+
+ // Returns the position of the scrollbar.
+ virtual int GetPosition() const = 0;
+
+ // Get the width or height of this scrollbar, for use in layout calculations.
+ // For a vertical scrollbar, this is the width of the scrollbar, likewise it
+ // is the height for a horizontal scrollbar.
+ virtual int GetLayoutSize() const = 0;
+
+ protected:
+ // Create new scrollbar, either horizontal or vertical. These are protected
+ // since you need to be creating either a NativeScrollBar or a
+ // BitmapScrollBar.
+ ScrollBar(bool is_horiz);
+
+ private:
+ const bool is_horiz_;
+
+ // Current controller
+ ScrollBarController* controller_;
+
+ // properties
+ int max_pos_;
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_CONTROLS_SCROLLBAR_SCROLLBAR_H_