diff options
-rw-r--r-- | views/controls/scrollbar/base_scroll_bar.cc | 383 | ||||
-rw-r--r-- | views/controls/scrollbar/base_scroll_bar.h | 156 | ||||
-rw-r--r-- | views/controls/scrollbar/base_scroll_bar_button.cc | 47 | ||||
-rw-r--r-- | views/controls/scrollbar/base_scroll_bar_button.h | 50 | ||||
-rw-r--r-- | views/controls/scrollbar/base_scroll_bar_thumb.cc | 127 | ||||
-rw-r--r-- | views/controls/scrollbar/base_scroll_bar_thumb.h | 82 | ||||
-rw-r--r-- | views/controls/scrollbar/bitmap_scroll_bar.cc | 512 | ||||
-rw-r--r-- | views/controls/scrollbar/bitmap_scroll_bar.h | 130 | ||||
-rw-r--r-- | views/controls/scrollbar/native_scroll_bar_gtk.cc | 6 | ||||
-rw-r--r-- | views/controls/scrollbar/native_scroll_bar_views.cc | 371 | ||||
-rw-r--r-- | views/controls/scrollbar/native_scroll_bar_views.h | 76 | ||||
-rw-r--r-- | views/controls/scrollbar/native_scroll_bar_wrapper.h | 4 | ||||
-rw-r--r-- | views/controls/scrollbar/scrollbar_unittest.cc | 150 | ||||
-rw-r--r-- | views/views.gyp | 9 |
14 files changed, 1513 insertions, 590 deletions
diff --git a/views/controls/scrollbar/base_scroll_bar.cc b/views/controls/scrollbar/base_scroll_bar.cc new file mode 100644 index 0000000..d18d4fd --- /dev/null +++ b/views/controls/scrollbar/base_scroll_bar.cc @@ -0,0 +1,383 @@ +// Copyright (c) 2011 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/base_scroll_bar.h" + +#if defined(OS_LINUX) +#include "ui/gfx/screen.h" +#endif + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/message_loop.h" +#include "base/string16.h" +#include "base/utf_string_conversions.h" +#include "grit/ui_strings.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/canvas.h" +#include "views/controls/menu/menu.h" +#include "views/controls/scrollbar/base_scroll_bar_thumb.h" +#include "views/controls/scroll_view.h" +#include "views/widget/widget.h" + +#undef min +#undef max + +namespace views { + +/////////////////////////////////////////////////////////////////////////////// +// BaseScrollBar, public: + +BaseScrollBar::BaseScrollBar(bool horizontal, BaseScrollBarThumb* thumb) + : ScrollBar(horizontal), + thumb_(thumb), + contents_size_(0), + contents_scroll_offset_(0), + thumb_track_state_(CustomButton::BS_NORMAL), + last_scroll_amount_(SCROLL_NONE), + ALLOW_THIS_IN_INITIALIZER_LIST(repeater_( + NewCallback<BaseScrollBar>(this, + &BaseScrollBar::TrackClicked))), + context_menu_mouse_position_(0) { + AddChildView(thumb_); + + set_context_menu_controller(this); + thumb_->set_context_menu_controller(this); +} + +void BaseScrollBar::ScrollByAmount(ScrollAmount amount) { + int offset = contents_scroll_offset_; + switch (amount) { + case SCROLL_START: + offset = GetMinPosition(); + break; + case SCROLL_END: + offset = GetMaxPosition(); + break; + case SCROLL_PREV_LINE: + offset -= GetScrollIncrement(false, false); + offset = std::max(GetMinPosition(), offset); + break; + case SCROLL_NEXT_LINE: + offset += GetScrollIncrement(false, true); + offset = std::min(GetMaxPosition(), offset); + break; + case SCROLL_PREV_PAGE: + offset -= GetScrollIncrement(true, false); + offset = std::max(GetMinPosition(), offset); + break; + case SCROLL_NEXT_PAGE: + offset += GetScrollIncrement(true, true); + offset = std::min(GetMaxPosition(), offset); + break; + default: + break; + } + contents_scroll_offset_ = offset; + ScrollContentsToOffset(); +} + +void BaseScrollBar::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 BaseScrollBar::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(); +} + +/////////////////////////////////////////////////////////////////////////////// +// BaseScrollBar, View implementation: + +bool BaseScrollBar::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 BaseScrollBar::OnMouseReleased(const MouseEvent& event) { + OnMouseCaptureLost(); +} + +void BaseScrollBar::OnMouseCaptureLost() { + SetThumbTrackState(CustomButton::BS_NORMAL); + repeater_.Stop(); +} + +bool BaseScrollBar::OnKeyPressed(const KeyEvent& event) { + ScrollAmount amount = SCROLL_NONE; + switch (event.key_code()) { + case ui::VKEY_UP: + if (!IsHorizontal()) + amount = SCROLL_PREV_LINE; + break; + case ui::VKEY_DOWN: + if (!IsHorizontal()) + amount = SCROLL_NEXT_LINE; + break; + case ui::VKEY_LEFT: + if (IsHorizontal()) + amount = SCROLL_PREV_LINE; + break; + case ui::VKEY_RIGHT: + if (IsHorizontal()) + amount = SCROLL_NEXT_LINE; + break; + case ui::VKEY_PRIOR: + amount = SCROLL_PREV_PAGE; + break; + case ui::VKEY_NEXT: + amount = SCROLL_NEXT_PAGE; + break; + case ui::VKEY_HOME: + amount = SCROLL_START; + break; + case ui::VKEY_END: + amount = SCROLL_END; + break; + default: + break; + } + if (amount != SCROLL_NONE) { + ScrollByAmount(amount); + return true; + } + return false; +} + +bool BaseScrollBar::OnMouseWheel(const MouseWheelEvent& event) { + ScrollByContentsOffset(event.offset()); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// BaseScrollBar, ContextMenuController implementation: + +enum ScrollBarContextMenuCommands { + ScrollBarContextMenuCommand_ScrollHere = 1, + ScrollBarContextMenuCommand_ScrollStart, + ScrollBarContextMenuCommand_ScrollEnd, + ScrollBarContextMenuCommand_ScrollPageUp, + ScrollBarContextMenuCommand_ScrollPageDown, + ScrollBarContextMenuCommand_ScrollPrev, + ScrollBarContextMenuCommand_ScrollNext +}; + +void BaseScrollBar::ShowContextMenuForView(View* source, + const gfx::Point& p, + bool is_mouse_gesture) { + Widget* widget = GetWidget(); + gfx::Rect widget_bounds = widget->GetWindowScreenBounds(); + gfx::Point temp_pt(p.x() - widget_bounds.x(), p.y() - widget_bounds.y()); + View::ConvertPointFromWidget(this, &temp_pt); + context_menu_mouse_position_ = IsHorizontal() ? temp_pt.x() : temp_pt.y(); + + scoped_ptr<Menu> menu( + Menu::Create(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(p.x(), p.y()); +} + +/////////////////////////////////////////////////////////////////////////////// +// BaseScrollBar, Menu::Delegate implementation: + +std::wstring BaseScrollBar::GetLabel(int id) const { + int ids_value = 0; + switch (id) { + case ScrollBarContextMenuCommand_ScrollHere: + ids_value = IDS_APP_SCROLLBAR_CXMENU_SCROLLHERE; + break; + case ScrollBarContextMenuCommand_ScrollStart: + ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLLEFTEDGE + : IDS_APP_SCROLLBAR_CXMENU_SCROLLHOME; + break; + case ScrollBarContextMenuCommand_ScrollEnd: + ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLRIGHTEDGE + : IDS_APP_SCROLLBAR_CXMENU_SCROLLEND; + break; + case ScrollBarContextMenuCommand_ScrollPageUp: + ids_value = IDS_APP_SCROLLBAR_CXMENU_SCROLLPAGEUP; + break; + case ScrollBarContextMenuCommand_ScrollPageDown: + ids_value = IDS_APP_SCROLLBAR_CXMENU_SCROLLPAGEDOWN; + break; + case ScrollBarContextMenuCommand_ScrollPrev: + ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLLEFT + : IDS_APP_SCROLLBAR_CXMENU_SCROLLUP; + break; + case ScrollBarContextMenuCommand_ScrollNext: + ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLRIGHT + : IDS_APP_SCROLLBAR_CXMENU_SCROLLDOWN; + break; + default: + NOTREACHED() << "Invalid BaseScrollBar Context Menu command!"; + } + + return ids_value ? UTF16ToWide(l10n_util::GetStringUTF16(ids_value)) : L""; +} + +bool BaseScrollBar::IsCommandEnabled(int id) const { + switch (id) { + case ScrollBarContextMenuCommand_ScrollPageUp: + case ScrollBarContextMenuCommand_ScrollPageDown: + return !IsHorizontal(); + } + return true; +} + +void BaseScrollBar::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; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// BaseScrollBar, ScrollBar implementation: + +void BaseScrollBar::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 BaseScrollBar::GetPosition() const { + return thumb_->GetPosition(); +} + +/////////////////////////////////////////////////////////////////////////////// +// BaseScrollBar, protected: + +BaseScrollBarThumb* BaseScrollBar::GetThumb() const { + return thumb_; +} + +CustomButton::ButtonState BaseScrollBar::GetThumbTrackState() const { + return thumb_track_state_; +} + +void BaseScrollBar::ScrollToPosition(int position) { + GetController()->ScrollToPosition(this, position); +} + +int BaseScrollBar::GetScrollIncrement(bool is_page, bool is_positive) { + return GetController()->GetScrollIncrement(this, is_page, is_positive); +} + + +/////////////////////////////////////////////////////////////////////////////// +// BaseScrollBar, private: + +void BaseScrollBar::TrackClicked() { + if (last_scroll_amount_ != SCROLL_NONE) + ScrollByAmount(last_scroll_amount_); +} + +void BaseScrollBar::ScrollContentsToOffset() { + ScrollToPosition(contents_scroll_offset_); + thumb_->SetPosition(CalculateThumbPosition(contents_scroll_offset_)); +} + +int BaseScrollBar::GetTrackSize() const { + gfx::Rect track_bounds = GetTrackBounds(); + return IsHorizontal() ? track_bounds.width() : track_bounds.height(); +} + +int BaseScrollBar::CalculateThumbPosition(int contents_scroll_offset) const { + return (contents_scroll_offset * GetTrackSize()) / contents_size_; +} + +int BaseScrollBar::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 BaseScrollBar::SetThumbTrackState(CustomButton::ButtonState state) { + thumb_track_state_ = state; + SchedulePaint(); +} + +} // namespace views diff --git a/views/controls/scrollbar/base_scroll_bar.h b/views/controls/scrollbar/base_scroll_bar.h new file mode 100644 index 0000000..7dc037b --- /dev/null +++ b/views/controls/scrollbar/base_scroll_bar.h @@ -0,0 +1,156 @@ +// Copyright (c) 2011 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_BASE_SCROLL_BAR_H_ +#define VIEWS_CONTROLS_SCROLLBAR_BASE_SCROLL_BAR_H_ +#pragma once + +#include "views/context_menu_controller.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 { + +class BaseScrollBarThumb; + +/////////////////////////////////////////////////////////////////////////////// +// +// BaseScrollBar +// +/////////////////////////////////////////////////////////////////////////////// +class VIEWS_EXPORT BaseScrollBar : public ScrollBar, + public ContextMenuController, + public Menu::Delegate { + public: + BaseScrollBar(bool horizontal, BaseScrollBarThumb* thumb); + virtual ~BaseScrollBar() { } + + // Get the bounds of the "track" area that the thumb is free to slide within. + virtual gfx::Rect GetTrackBounds() const = 0; + + // 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() OVERRIDE = 0; + virtual void Layout() OVERRIDE = 0; + virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE; + virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE; + virtual void OnMouseCaptureLost() OVERRIDE; + virtual bool OnKeyPressed(const KeyEvent& event) OVERRIDE; + virtual bool OnMouseWheel(const MouseWheelEvent& event) OVERRIDE; + + // ScrollBar overrides: + virtual void Update(int viewport_size, + int content_size, + int contents_scroll_offset) OVERRIDE; + virtual int GetLayoutSize() const OVERRIDE = 0; + virtual int GetPosition() const OVERRIDE; + + // ContextMenuController overrides. + virtual void ShowContextMenuForView(View* source, + const gfx::Point& p, + bool is_mouse_gesture) OVERRIDE; + + // Menu::Delegate overrides: + virtual std::wstring GetLabel(int id) const OVERRIDE; + virtual bool IsCommandEnabled(int id) const OVERRIDE; + virtual void ExecuteCommand(int id) OVERRIDE; + + protected: + // View overrides: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE = 0; + + BaseScrollBarThumb* GetThumb() const; + + CustomButton::ButtonState GetThumbTrackState() const; + + // Wrapper functions that calls the controller. We need this since native + // scrollbars wrap around a different scrollbar. When calling the controller + // we need to pass in the appropriate scrollbar. For normal scrollbars it's + // the |this| scrollbar, for native scrollbars it's the native scrollbar used + // to create this. + virtual void ScrollToPosition(int position); + virtual int GetScrollIncrement(bool is_page, bool is_positive); + + 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); + + BaseScrollBarThumb* thumb_; + + // 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_; + + // 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_; + + DISALLOW_COPY_AND_ASSIGN(BaseScrollBar); +}; + +} // namespace views + +#endif // VIEWS_CONTROLS_SCROLLBAR_BASE_SCROLL_BAR_H_ diff --git a/views/controls/scrollbar/base_scroll_bar_button.cc b/views/controls/scrollbar/base_scroll_bar_button.cc new file mode 100644 index 0000000..95239e7 --- /dev/null +++ b/views/controls/scrollbar/base_scroll_bar_button.cc @@ -0,0 +1,47 @@ +// Copyright (c) 2011 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/base_scroll_bar_button.h" + +namespace views { + +BaseScrollBarButton::BaseScrollBarButton(ButtonListener* listener) + : CustomButton(listener), + ALLOW_THIS_IN_INITIALIZER_LIST(repeater_( + NewCallback<BaseScrollBarButton>(this, + &BaseScrollBarButton::NotifyClick))) { +} + +BaseScrollBarButton::~BaseScrollBarButton() { +} + +bool BaseScrollBarButton::OnMousePressed(const MouseEvent& event) { + Button::NotifyClick(event); + repeater_.Start(); + return true; +} + +void BaseScrollBarButton::OnMouseReleased(const MouseEvent& event) { + OnMouseCaptureLost(); +} + +void BaseScrollBarButton::OnMouseCaptureLost() { + repeater_.Stop(); +} + +void BaseScrollBarButton::NotifyClick() { +#if defined(OS_WIN) + DWORD pos = GetMessagePos(); + POINTS points = MAKEPOINTS(pos); + gfx::Point cursor_point(points.x, points.y); +#elif defined(OS_LINUX) + gfx::Point cursor_point = gfx::Screen::GetCursorScreenPoint(); +#endif + views::MouseEvent event(ui::ET_MOUSE_RELEASED, + cursor_point.x(), cursor_point.y(), + ui::EF_LEFT_BUTTON_DOWN); + Button::NotifyClick(event); +} + +} // namespace views diff --git a/views/controls/scrollbar/base_scroll_bar_button.h b/views/controls/scrollbar/base_scroll_bar_button.h new file mode 100644 index 0000000..422ebfa --- /dev/null +++ b/views/controls/scrollbar/base_scroll_bar_button.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011 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_BASE_SCROLL_BAR_BUTTON_H_ +#define VIEWS_CONTROLS_SCROLLBAR_BASE_SCROLL_BAR_BUTTON_H_ +#pragma once + +#include "views/controls/button/custom_button.h" + +#include "views/repeat_controller.h" + +#if defined(OS_LINUX) +#include "ui/gfx/screen.h" +#endif + +namespace views { + +/////////////////////////////////////////////////////////////////////////////// +// +// ScrollBarButton +// +// 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 VIEWS_EXPORT BaseScrollBarButton : public CustomButton { + public: + explicit BaseScrollBarButton(ButtonListener* listener); + virtual ~BaseScrollBarButton(); + + protected: + virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE; + virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE; + virtual void OnMouseCaptureLost() OVERRIDE; + + private: + void NotifyClick(); + + // The repeat controller that we use to repeatedly click the button when the + // mouse button is down. + RepeatController repeater_; + + DISALLOW_COPY_AND_ASSIGN(BaseScrollBarButton); +}; + +} // namespace views + +#endif // VIEWS_CONTROLS_SCROLLBAR_BASE_SCROLL_BAR_BUTTON_H_ diff --git a/views/controls/scrollbar/base_scroll_bar_thumb.cc b/views/controls/scrollbar/base_scroll_bar_thumb.cc new file mode 100644 index 0000000..bbd42f9 --- /dev/null +++ b/views/controls/scrollbar/base_scroll_bar_thumb.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2011 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/base_scroll_bar_thumb.h" + +#include "views/controls/scrollbar/base_scroll_bar.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/rect.h" + +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; +} + +namespace views { + +BaseScrollBarThumb::BaseScrollBarThumb(BaseScrollBar* scroll_bar) + : scroll_bar_(scroll_bar), + drag_start_position_(-1), + mouse_offset_(-1), + state_(CustomButton::BS_NORMAL) { +} + +BaseScrollBarThumb::~BaseScrollBarThumb() { +} + +void BaseScrollBarThumb::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, 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); + } + SetBoundsRect(thumb_bounds); +} + +int BaseScrollBarThumb::GetSize() const { + if (scroll_bar_->IsHorizontal()) + return width(); + return height(); +} + +void BaseScrollBarThumb::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_y(track_bounds.y() + position); + } + SetBoundsRect(thumb_bounds); +} + +int BaseScrollBarThumb::GetPosition() const { + gfx::Rect track_bounds = scroll_bar_->GetTrackBounds(); + if (scroll_bar_->IsHorizontal()) + return x() - track_bounds.x(); + return y() - track_bounds.y(); +} + +void BaseScrollBarThumb::OnMouseEntered(const MouseEvent& event) { + SetState(CustomButton::BS_HOT); +} + +void BaseScrollBarThumb::OnMouseExited(const MouseEvent& event) { + SetState(CustomButton::BS_NORMAL); +} + +bool BaseScrollBarThumb::OnMousePressed(const MouseEvent& event) { + mouse_offset_ = scroll_bar_->IsHorizontal() ? event.x() : event.y(); + drag_start_position_ = GetPosition(); + SetState(CustomButton::BS_PUSHED); + return true; +} + +bool BaseScrollBarThumb::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(GetPosition() + thumb_x, false); + } else { + int thumb_y = event.y() - mouse_offset_; + scroll_bar_->ScrollToThumbPosition(GetPosition() + thumb_y, false); + } + return true; +} + +void BaseScrollBarThumb::OnMouseReleased(const MouseEvent& event) { + OnMouseCaptureLost(); +} + +void BaseScrollBarThumb::OnMouseCaptureLost() { + SetState(CustomButton::BS_HOT); +} + +CustomButton::ButtonState BaseScrollBarThumb::GetState() const { + return state_; +} + +void BaseScrollBarThumb::SetState(CustomButton::ButtonState state) { + state_ = state; + SchedulePaint(); +} + +} // namespace views diff --git a/views/controls/scrollbar/base_scroll_bar_thumb.h b/views/controls/scrollbar/base_scroll_bar_thumb.h new file mode 100644 index 0000000..407b830 --- /dev/null +++ b/views/controls/scrollbar/base_scroll_bar_thumb.h @@ -0,0 +1,82 @@ +// Copyright (c) 2011 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_BASE_SCROLL_BAR_THUMB_H_ +#define VIEWS_CONTROLS_SCROLLBAR_BASE_SCROLL_BAR_THUMB_H_ +#pragma once + +#include "ui/gfx/size.h" +#include "views/controls/button/custom_button.h" +#include "views/controls/scrollbar/scroll_bar.h" +#include "views/view.h" + +namespace gfx { +class Canvas; +} + +namespace views { + +class BaseScrollBar; + +/////////////////////////////////////////////////////////////////////////////// +// +// BaseScrollBarThumb +// +// 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 VIEWS_EXPORT BaseScrollBarThumb : public View { + public: + explicit BaseScrollBarThumb(BaseScrollBar* scroll_bar); + virtual ~BaseScrollBarThumb(); + + // Sets the size (width or height) of the thumb to the specified value. + void SetSize(int size); + + // Retrieves the size (width or height) of the thumb. + int GetSize() const; + + // Sets the position of the thumb on the x or y axis. + void SetPosition(int position); + + // Gets the position of the thumb on the x or y axis. + int GetPosition() const; + + // View overrides: + virtual gfx::Size GetPreferredSize() OVERRIDE = 0; + + protected: + // View overrides: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE = 0; + virtual void OnMouseEntered(const MouseEvent& event) OVERRIDE; + virtual void OnMouseExited(const MouseEvent& event) OVERRIDE; + virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE; + virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE; + virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE; + virtual void OnMouseCaptureLost() OVERRIDE; + + CustomButton::ButtonState GetState() const; + // Update our state and schedule a repaint when the mouse moves over us. + void SetState(CustomButton::ButtonState state); + + private: + // The BaseScrollBar that owns us. + BaseScrollBar* 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_COPY_AND_ASSIGN(BaseScrollBarThumb); +}; + +} // namespace views + +#endif // VIEWS_CONTROLS_SCROLLBAR_BASE_SCROLL_BAR_THUMB_H_ diff --git a/views/controls/scrollbar/bitmap_scroll_bar.cc b/views/controls/scrollbar/bitmap_scroll_bar.cc index b52e8d9..6b7fbc0 100644 --- a/views/controls/scrollbar/bitmap_scroll_bar.cc +++ b/views/controls/scrollbar/bitmap_scroll_bar.cc @@ -19,6 +19,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/canvas.h" #include "views/controls/menu/menu.h" +#include "views/controls/scrollbar/base_scroll_bar_thumb.h" #include "views/controls/scroll_view.h" #include "views/widget/widget.h" @@ -44,7 +45,7 @@ static const int kScrollThumbDragOutSnap = 100; /////////////////////////////////////////////////////////////////////////////// class AutorepeatButton : public ImageButton { public: - AutorepeatButton(ButtonListener* listener) + explicit AutorepeatButton(ButtonListener* listener) : ImageButton(listener), ALLOW_THIS_IN_INITIALIZER_LIST(repeater_( NewCallback<AutorepeatButton>(this, @@ -97,59 +98,14 @@ class AutorepeatButton : public ImageButton { // drag to scroll the associated contents view within the viewport. // /////////////////////////////////////////////////////////////////////////////// -class BitmapScrollBarThumb : public View { +class BitmapScrollBarThumb : public BaseScrollBarThumb { public: explicit BitmapScrollBarThumb(BitmapScrollBar* scroll_bar) - : scroll_bar_(scroll_bar), - drag_start_position_(-1), - mouse_offset_(-1), - state_(CustomButton::BS_NORMAL) { + : BaseScrollBarThumb(scroll_bar), + scroll_bar_(scroll_bar) { } 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, 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); - } - SetBoundsRect(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_y(track_bounds.y() + position); - } - SetBoundsRect(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() OVERRIDE { return gfx::Size(background_bitmap()->width(), @@ -160,7 +116,7 @@ class BitmapScrollBarThumb : public View { protected: // View overrides: - virtual void Paint(gfx::Canvas* canvas) OVERRIDE { + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { canvas->DrawBitmapInt(*start_cap_bitmap(), 0, 0); int top_cap_height = start_cap_bitmap()->height(); int bottom_cap_height = end_cap_bitmap()->height(); @@ -176,71 +132,21 @@ class BitmapScrollBarThumb : public View { canvas->DrawBitmapInt(*grippy_bitmap(), grippy_x, grippy_y); } - virtual void OnMouseEntered(const MouseEvent& event) OVERRIDE { - SetState(CustomButton::BS_HOT); - } - - virtual void OnMouseExited(const MouseEvent& event) OVERRIDE { - SetState(CustomButton::BS_NORMAL); - } - - virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE { - 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) OVERRIDE { - // 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) OVERRIDE { - OnMouseCaptureLost(); - } - - virtual void OnMouseCaptureLost() OVERRIDE { - SetState(CustomButton::BS_HOT); - } - 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_]; + return scroll_bar_->images_[BitmapScrollBar::THUMB_START_CAP][GetState()]; } // Returns the bitmap rendered at the end of the thumb. SkBitmap* end_cap_bitmap() const { - return scroll_bar_->images_[BitmapScrollBar::THUMB_END_CAP][state_]; + return scroll_bar_->images_[BitmapScrollBar::THUMB_END_CAP][GetState()]; } // 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_]; + return scroll_bar_->images_[BitmapScrollBar::THUMB_MIDDLE][GetState()]; } // Returns the bitmap that is rendered in the middle of the thumb @@ -250,24 +156,9 @@ class BitmapScrollBarThumb : public View { [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_COPY_AND_ASSIGN(BitmapScrollBarThumb); }; @@ -277,19 +168,10 @@ class BitmapScrollBarThumb : public View { // BitmapScrollBar, public: BitmapScrollBar::BitmapScrollBar(bool horizontal, bool show_scroll_buttons) - : contents_size_(0), - contents_scroll_offset_(0), + : BaseScrollBar(horizontal, new BitmapScrollBarThumb(this)), ALLOW_THIS_IN_INITIALIZER_LIST(prev_button_(new AutorepeatButton(this))), ALLOW_THIS_IN_INITIALIZER_LIST(next_button_(new AutorepeatButton(this))), - ALLOW_THIS_IN_INITIALIZER_LIST(thumb_(new BitmapScrollBarThumb(this))), - thumb_track_state_(CustomButton::BS_NORMAL), - last_scroll_amount_(SCROLL_NONE), - ALLOW_THIS_IN_INITIALIZER_LIST(repeater_( - NewCallback<BitmapScrollBar>(this, - &BitmapScrollBar::TrackClicked))), - context_menu_mouse_position_(0), - show_scroll_buttons_(show_scroll_buttons), - ScrollBar(horizontal) { + show_scroll_buttons_(show_scroll_buttons) { if (!show_scroll_buttons_) { prev_button_->SetVisible(false); next_button_->SetVisible(false); @@ -297,29 +179,10 @@ BitmapScrollBar::BitmapScrollBar(bool horizontal, bool show_scroll_buttons) AddChildView(prev_button_); AddChildView(next_button_); - AddChildView(thumb_); set_context_menu_controller(this); prev_button_->set_context_menu_controller(this); next_button_->set_context_menu_controller(this); - thumb_->set_context_menu_controller(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, 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, @@ -344,58 +207,26 @@ void BitmapScrollBar::SetImage(ScrollBarPart part, } } -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(); +int BitmapScrollBar::GetLayoutSize() const { + gfx::Size prefsize = prev_button_->GetPreferredSize(); + return IsHorizontal() ? prefsize.height() : prefsize.width(); } -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(); +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, width() - (prefsize.width() * 2)); + gfx::Rect track_bounds(prefsize.width(), 0, new_width, prefsize.height()); + return track_bounds; } - ScrollContentsToOffset(); + 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; } /////////////////////////////////////////////////////////////////////////////// @@ -426,19 +257,20 @@ void BitmapScrollBar::Layout() { next_button_->SetBounds(0, 0, 0, 0); } + BaseScrollBarThumb* thumb = GetThumb(); // Size and place the thumb - gfx::Size thumb_prefsize = thumb_->GetPreferredSize(); + 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->SetBounds(thumb->x(), thumb->y(), thumb->width(), thumb_prefsize.height()); } else { - thumb_->SetBounds(thumb_->x(), thumb_->y(), thumb_prefsize.width(), - thumb_->height()); + 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 @@ -446,196 +278,21 @@ void BitmapScrollBar::Layout() { // 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); + 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) { - OnMouseCaptureLost(); -} - -void BitmapScrollBar::OnMouseCaptureLost() { - SetThumbTrackState(CustomButton::BS_NORMAL); - repeater_.Stop(); -} - -bool BitmapScrollBar::OnKeyPressed(const KeyEvent& event) { - ScrollAmount amount = SCROLL_NONE; - switch (event.key_code()) { - case ui::VKEY_UP: - if (!IsHorizontal()) - amount = SCROLL_PREV_LINE; - break; - case ui::VKEY_DOWN: - if (!IsHorizontal()) - amount = SCROLL_NEXT_LINE; - break; - case ui::VKEY_LEFT: - if (IsHorizontal()) - amount = SCROLL_PREV_LINE; - break; - case ui::VKEY_RIGHT: - if (IsHorizontal()) - amount = SCROLL_NEXT_LINE; - break; - case ui::VKEY_PRIOR: - amount = SCROLL_PREV_PAGE; - break; - case ui::VKEY_NEXT: - amount = SCROLL_NEXT_PAGE; - break; - case ui::VKEY_HOME: - amount = SCROLL_START; - break; - case ui::VKEY_END: - amount = SCROLL_END; - break; - } - if (amount != SCROLL_NONE) { - ScrollByAmount(amount); - return true; - } - return false; -} - -bool BitmapScrollBar::OnMouseWheel(const MouseWheelEvent& event) { - ScrollByContentsOffset(event.offset()); - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// BitmapScrollBar, ContextMenuController implementation: - -enum ScrollBarContextMenuCommands { - ScrollBarContextMenuCommand_ScrollHere = 1, - ScrollBarContextMenuCommand_ScrollStart, - ScrollBarContextMenuCommand_ScrollEnd, - ScrollBarContextMenuCommand_ScrollPageUp, - ScrollBarContextMenuCommand_ScrollPageDown, - ScrollBarContextMenuCommand_ScrollPrev, - ScrollBarContextMenuCommand_ScrollNext -}; - -void BitmapScrollBar::ShowContextMenuForView(View* source, - const gfx::Point& p, - bool is_mouse_gesture) { - Widget* widget = GetWidget(); - gfx::Rect widget_bounds = widget->GetWindowScreenBounds(); - gfx::Point temp_pt(p.x() - widget_bounds.x(), p.y() - widget_bounds.y()); - View::ConvertPointFromWidget(this, &temp_pt); - context_menu_mouse_position_ = IsHorizontal() ? temp_pt.x() : temp_pt.y(); - - scoped_ptr<Menu> menu( - Menu::Create(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(p.x(), p.y()); -} - /////////////////////////////////////////////////////////////////////////////// -// BitmapScrollBar, Menu::Delegate implementation: - -std::wstring BitmapScrollBar::GetLabel(int id) const { - int ids_value = 0; - switch (id) { - case ScrollBarContextMenuCommand_ScrollHere: - ids_value = IDS_APP_SCROLLBAR_CXMENU_SCROLLHERE; - break; - case ScrollBarContextMenuCommand_ScrollStart: - ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLLEFTEDGE - : IDS_APP_SCROLLBAR_CXMENU_SCROLLHOME; - break; - case ScrollBarContextMenuCommand_ScrollEnd: - ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLRIGHTEDGE - : IDS_APP_SCROLLBAR_CXMENU_SCROLLEND; - break; - case ScrollBarContextMenuCommand_ScrollPageUp: - ids_value = IDS_APP_SCROLLBAR_CXMENU_SCROLLPAGEUP; - break; - case ScrollBarContextMenuCommand_ScrollPageDown: - ids_value = IDS_APP_SCROLLBAR_CXMENU_SCROLLPAGEDOWN; - break; - case ScrollBarContextMenuCommand_ScrollPrev: - ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLLEFT - : IDS_APP_SCROLLBAR_CXMENU_SCROLLUP; - break; - case ScrollBarContextMenuCommand_ScrollNext: - ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLRIGHT - : IDS_APP_SCROLLBAR_CXMENU_SCROLLDOWN; - break; - default: - NOTREACHED() << "Invalid BitmapScrollBar Context Menu command!"; - } - - return ids_value ? UTF16ToWide(l10n_util::GetStringUTF16(ids_value)) : L""; -} - -bool BitmapScrollBar::IsCommandEnabled(int id) const { - switch (id) { - case ScrollBarContextMenuCommand_ScrollPageUp: - case ScrollBarContextMenuCommand_ScrollPageDown: - return !IsHorizontal(); - } - return true; -} +// BitmapScrollBar, View implementation: -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; - } +void BitmapScrollBar::OnPaint(gfx::Canvas* canvas) { + // Paint the track. + gfx::Rect track_bounds = GetTrackBounds(); + canvas->TileImageInt(*images_[THUMB_TRACK][GetThumbTrackState()], + track_bounds.x(), track_bounds.y(), + track_bounds.width(), track_bounds.height()); } /////////////////////////////////////////////////////////////////////////////// @@ -649,87 +306,4 @@ void BitmapScrollBar::ButtonPressed(Button* sender, const views::Event& event) { } } -/////////////////////////////////////////////////////////////////////////////// -// 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, View implementation: - -void BitmapScrollBar::OnPaint(gfx::Canvas* 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()); -} - -/////////////////////////////////////////////////////////////////////////////// -// BitmapScrollBar, private: - -void BitmapScrollBar::TrackClicked() { - if (last_scroll_amount_ != SCROLL_NONE) - ScrollByAmount(last_scroll_amount_); -} - -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 index ce072bf..9b5cc91 100644 --- a/views/controls/scrollbar/bitmap_scroll_bar.h +++ b/views/controls/scrollbar/bitmap_scroll_bar.h @@ -6,11 +6,7 @@ #define VIEWS_CONTROLS_SCROLLBAR_BITMAP_SCROLL_BAR_H_ #pragma once -#include "views/context_menu_controller.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" +#include "views/controls/scrollbar/base_scroll_bar.h" namespace views { @@ -27,24 +23,13 @@ class BitmapScrollBarThumb; // 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 { +class VIEWS_EXPORT BitmapScrollBar : public BaseScrollBar, + public ButtonListener { 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. @@ -71,122 +56,31 @@ class BitmapScrollBar : public ScrollBar, 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); + gfx::Rect GetTrackBounds() const; + protected: // View overrides: virtual gfx::Size GetPreferredSize() OVERRIDE; virtual void Layout() OVERRIDE; - virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE; - virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE; - virtual void OnMouseCaptureLost() OVERRIDE; - virtual bool OnKeyPressed(const KeyEvent& event) OVERRIDE; - virtual bool OnMouseWheel(const MouseWheelEvent& event) OVERRIDE; - - // BaseButton::ButtonListener overrides: - virtual void ButtonPressed(Button* sender, - const views::Event& event) OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; // ScrollBar overrides: - virtual void Update(int viewport_size, - int content_size, - int contents_scroll_offset) OVERRIDE; virtual int GetLayoutSize() const OVERRIDE; - virtual int GetPosition() const OVERRIDE; - - // ContextMenuController overrides. - virtual void ShowContextMenuForView(View* source, - const gfx::Point& p, - bool is_mouse_gesture) OVERRIDE; - - // Menu::Delegate overrides: - virtual std::wstring GetLabel(int id) const OVERRIDE; - virtual bool IsCommandEnabled(int id) const OVERRIDE; - virtual void ExecuteCommand(int id) OVERRIDE; - protected: - // View overrides: - virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + // BaseButton::ButtonListener overrides: + virtual void ButtonPressed(Button* sender, + const views::Event& event) OVERRIDE; 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); + // Up/Down/Left/Right buttons. + ImageButton* prev_button_; + ImageButton* next_button_; // 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_; diff --git a/views/controls/scrollbar/native_scroll_bar_gtk.cc b/views/controls/scrollbar/native_scroll_bar_gtk.cc index dc1a9b6..cd9bebd 100644 --- a/views/controls/scrollbar/native_scroll_bar_gtk.cc +++ b/views/controls/scrollbar/native_scroll_bar_gtk.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -8,7 +8,9 @@ #include "ui/base/keycodes/keyboard_codes_posix.h" #include "views/controls/scrollbar/native_scroll_bar.h" +#include "views/controls/scrollbar/native_scroll_bar_views.h" #include "views/controls/scrollbar/scroll_bar.h" +#include "views/widget/widget.h" namespace views { @@ -199,6 +201,8 @@ void NativeScrollBarGtk::MoveToBottom() { // static NativeScrollBarWrapper* NativeScrollBarWrapper::CreateWrapper( NativeScrollBar* scroll_bar) { + if (Widget::IsPureViews()) + return new NativeScrollBarViews(scroll_bar); return new NativeScrollBarGtk(scroll_bar); } diff --git a/views/controls/scrollbar/native_scroll_bar_views.cc b/views/controls/scrollbar/native_scroll_bar_views.cc new file mode 100644 index 0000000..775c23c --- /dev/null +++ b/views/controls/scrollbar/native_scroll_bar_views.cc @@ -0,0 +1,371 @@ +// Copyright (c) 2011 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_views.h" + +#include "base/logging.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/path.h" +#include "views/controls/button/custom_button.h" +#include "views/controls/focusable_border.h" +#include "views/controls/scrollbar/native_scroll_bar.h" +#include "views/controls/scrollbar/scroll_bar.h" +#include "views/controls/scrollbar/base_scroll_bar_button.h" +#include "views/controls/scrollbar/base_scroll_bar_thumb.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; + + protected: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + + private: + gfx::NativeTheme::ExtraParams GetNativeThemeParams() const; + gfx::NativeTheme::Part GetNativeThemePart() const; + gfx::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; + + protected: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + + private: + gfx::NativeTheme::ExtraParams GetNativeThemeParams() const; + gfx::NativeTheme::Part GetNativeThemePart() const; + gfx::NativeTheme::State GetNativeThemeState() const; + + ScrollBar* scroll_bar_; +}; + +///////////////////////////////////////////////////////////////////////////// +// ScrollBarButton + +ScrollBarButton::ScrollBarButton( + ButtonListener* listener, + Type type) + : BaseScrollBarButton(listener), + type_(type) { +} + +ScrollBarButton::~ScrollBarButton() { +} + +gfx::Size ScrollBarButton::GetPreferredSize() { + const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance(); + return native_theme->GetPartSize(GetNativeThemePart(), + GetNativeThemeState(), + GetNativeThemeParams()); +} + +void ScrollBarButton::OnPaint(gfx::Canvas* canvas) { + const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance(); + gfx::Rect bounds; + bounds.set_size(GetPreferredSize()); + + native_theme->Paint(canvas->AsCanvasSkia(), + GetNativeThemePart(), + GetNativeThemeState(), + bounds, + GetNativeThemeParams()); +} + +gfx::NativeTheme::ExtraParams + ScrollBarButton::GetNativeThemeParams() const { + gfx::NativeTheme::ExtraParams params; + + switch (state_) { + case CustomButton::BS_HOT: + params.scrollbar_arrow.is_hovering = true; + break; + default: + params.scrollbar_arrow.is_hovering = false; + break; + } + + return params; +} + +gfx::NativeTheme::Part + ScrollBarButton::GetNativeThemePart() const { + switch (type_) { + case UP: + return gfx::NativeTheme::kScrollbarUpArrow; + case DOWN: + return gfx::NativeTheme::kScrollbarDownArrow; + case LEFT: + return gfx::NativeTheme::kScrollbarLeftArrow; + case RIGHT: + return gfx::NativeTheme::kScrollbarRightArrow; + default: + return gfx::NativeTheme::kScrollbarUpArrow; + } +} + +gfx::NativeTheme::State + ScrollBarButton::GetNativeThemeState() const { + gfx::NativeTheme::State state; + + switch (state_) { + case CustomButton::BS_HOT: + state = gfx::NativeTheme::kHovered; + break; + case CustomButton::BS_PUSHED: + state = gfx::NativeTheme::kPressed; + break; + case CustomButton::BS_DISABLED: + state = gfx::NativeTheme::kDisabled; + break; + case CustomButton::BS_NORMAL: + default: + state = gfx::NativeTheme::kNormal; + break; + } + + return state; +} + +///////////////////////////////////////////////////////////////////////////// +// ScrollBarThumb + +ScrollBarThumb::ScrollBarThumb(BaseScrollBar* scroll_bar) + : BaseScrollBarThumb(scroll_bar), + scroll_bar_(scroll_bar) { +} + +ScrollBarThumb::~ScrollBarThumb() { +} + +gfx::Size ScrollBarThumb::GetPreferredSize() { + const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance(); + return native_theme->GetPartSize(GetNativeThemePart(), + GetNativeThemeState(), + GetNativeThemeParams()); +} + +void ScrollBarThumb::OnPaint(gfx::Canvas* canvas) { + const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance(); + + native_theme->Paint(canvas->AsCanvasSkia(), + GetNativeThemePart(), + GetNativeThemeState(), + GetLocalBounds(), + GetNativeThemeParams()); +} + +gfx::NativeTheme::ExtraParams + ScrollBarThumb::GetNativeThemeParams() const { + gfx::NativeTheme::ExtraParams params; + + switch (GetState()) { + case CustomButton::BS_HOT: + params.scrollbar_thumb.is_hovering = true; + break; + default: + params.scrollbar_thumb.is_hovering = false; + break; + } + + return params; +} + +gfx::NativeTheme::Part ScrollBarThumb::GetNativeThemePart() const { + if (scroll_bar_->IsHorizontal()) + return gfx::NativeTheme::kScrollbarHorizontalThumb; + return gfx::NativeTheme::kScrollbarVerticalThumb; +} + +gfx::NativeTheme::State ScrollBarThumb::GetNativeThemeState() const { + gfx::NativeTheme::State state; + + switch (GetState()) { + case CustomButton::BS_HOT: + state = gfx::NativeTheme::kHovered; + break; + case CustomButton::BS_PUSHED: + state = gfx::NativeTheme::kPressed; + break; + case CustomButton::BS_DISABLED: + state = gfx::NativeTheme::kDisabled; + break; + case CustomButton::BS_NORMAL: + default: + state = gfx::NativeTheme::kNormal; + break; + } + + return state; +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// NativeScrollBarViews, public: + +NativeScrollBarViews::NativeScrollBarViews(NativeScrollBar* scroll_bar) + : BaseScrollBar(scroll_bar->IsHorizontal(), + new ScrollBarThumb(this)), + native_scroll_bar_(scroll_bar) { + SetController(native_scroll_bar_->GetController()); + + if (native_scroll_bar_->IsHorizontal()) { + prev_button_ = new ScrollBarButton(this, ScrollBarButton::LEFT); + next_button_ = new ScrollBarButton(this, ScrollBarButton::RIGHT); + + part_ = gfx::NativeTheme::kScrollbarHorizontalTrack; + } else { + prev_button_ = new ScrollBarButton(this, ScrollBarButton::UP); + next_button_ = new ScrollBarButton(this, ScrollBarButton::DOWN); + + part_ = gfx::NativeTheme::kScrollbarVerticalTrack; + } + + state_ = gfx::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() { + SetBoundsRect(native_scroll_bar_->GetLocalBounds()); + + 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) { + const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance(); + gfx::Rect bounds = GetTrackBounds(); + + 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(); + + + native_theme->Paint(canvas->AsCanvasSkia(), + part_, + state_, + bounds, + params_); +} + +gfx::Size NativeScrollBarViews::GetPreferredSize() { + if (native_scroll_bar_->IsHorizontal()) + return gfx::Size(0, GetHorizontalScrollBarHeight()); + return gfx::Size(GetVerticalScrollBarWidth(), 0); +} + +int NativeScrollBarViews::GetLayoutSize() const { + gfx::Size size = prev_button_->GetPreferredSize(); + return IsHorizontal() ? size.height() : size.width(); +} + +void NativeScrollBarViews::ScrollToPosition(int position) { + GetController()->ScrollToPosition(native_scroll_bar_, position); +} + +int NativeScrollBarViews::GetScrollIncrement(bool is_page, + bool is_positive) { + return GetController()->GetScrollIncrement(native_scroll_bar_, + is_page, + is_positive); +} + +////////////////////////////////////////////////////////////////////////////// +// BaseButton::ButtonListener overrides: + +void NativeScrollBarViews::ButtonPressed(Button* sender, + const views::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(bounds.width() - 2 * size.width()); + bounds.set_height(thumb->GetPreferredSize().height()); + } else { + bounds.set_y(bounds.y() + size.height()); + bounds.set_height(bounds.height() - 2 * size.height()); + bounds.set_width(thumb->GetPreferredSize().width()); + } + + return bounds; +} + +} // namespace views diff --git a/views/controls/scrollbar/native_scroll_bar_views.h b/views/controls/scrollbar/native_scroll_bar_views.h new file mode 100644 index 0000000..e863a1f --- /dev/null +++ b/views/controls/scrollbar/native_scroll_bar_views.h @@ -0,0 +1,76 @@ +// Copyright (c) 2011 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_SCROLL_BAR_VIEWS_H_ +#define VIEWS_CONTROLS_SCROLLBAR_NATIVE_SCROLL_BAR_VIEWS_H_ +#pragma once + +#include "ui/gfx/native_theme.h" +#include "ui/gfx/point.h" +#include "views/controls/button/button.h" +#include "views/controls/scrollbar/base_scroll_bar.h" +#include "views/controls/scrollbar/native_scroll_bar_wrapper.h" +#include "views/view.h" + +namespace gfx { +class Canvas; +} + +namespace views { + +class NativeScrollBar; + +// Views implementation for the scrollbar. +class VIEWS_EXPORT NativeScrollBarViews : public BaseScrollBar, + public ButtonListener, + public NativeScrollBarWrapper { + public: + // Creates new scrollbar, either horizontal or vertical. + explicit NativeScrollBarViews(NativeScrollBar* native_scroll_bar); + virtual ~NativeScrollBarViews(); + + private: + // View overrides: + virtual void Layout(); + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual gfx::Size GetPreferredSize(); + + // ScrollBar overrides: + virtual int GetLayoutSize() const OVERRIDE; + + // BaseScrollBar overrides: + virtual void ScrollToPosition(int position); + virtual int GetScrollIncrement(bool is_page, bool is_positive); + + // BaseButton::ButtonListener overrides: + virtual void ButtonPressed(Button* sender, + const views::Event& event) OVERRIDE; + + // NativeScrollBarWrapper overrides: + virtual int GetPosition() const; + virtual View* GetView(); + virtual void Update(int viewport_size, int content_size, int current_pos); + + // Returns the area for the track. This is the area of the scrollbar minus + // the size of the arrow buttons. + gfx::Rect GetTrackBounds() const; + + // The NativeScrollBar we are bound to. + NativeScrollBar* native_scroll_bar_; + + // The scroll bar buttons (Up/Down, Left/Right). + Button* prev_button_; + Button* next_button_; + + gfx::NativeTheme::ExtraParams params_; + gfx::NativeTheme::Part part_; + gfx::NativeTheme::State state_; + + DISALLOW_COPY_AND_ASSIGN(NativeScrollBarViews); +}; + +} // namespace views + +#endif // VIEWS_CONTROLS_SCROLLBAR_NATIVE_SCROLL_BAR_VIEWS_H_ + diff --git a/views/controls/scrollbar/native_scroll_bar_wrapper.h b/views/controls/scrollbar/native_scroll_bar_wrapper.h index fb92c3d..210401a 100644 --- a/views/controls/scrollbar/native_scroll_bar_wrapper.h +++ b/views/controls/scrollbar/native_scroll_bar_wrapper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -13,7 +13,7 @@ class View; // A specialization of NativeControlWrapper that hosts a platform-native // scroll bar. -class NativeScrollBarWrapper { +class VIEWS_EXPORT NativeScrollBarWrapper { public: virtual ~NativeScrollBarWrapper() {} diff --git a/views/controls/scrollbar/scrollbar_unittest.cc b/views/controls/scrollbar/scrollbar_unittest.cc new file mode 100644 index 0000000..a19bfe6 --- /dev/null +++ b/views/controls/scrollbar/scrollbar_unittest.cc @@ -0,0 +1,150 @@ +// Copyright (c) 2011 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" +#include "views/controls/scrollbar/native_scroll_bar.h" +#include "views/controls/scrollbar/native_scroll_bar_views.h" +#include "views/test/views_test_base.h" +#include "views/widget/widget.h" + +namespace { + +// The Scrollbar controller. This is the widget that should do the real +// scrolling of contents. +class TestScrollBarController : public views::ScrollBarController { + public: + virtual void ScrollToPosition(views::ScrollBar* source, + int position) OVERRIDE { + last_source = source; + last_position = position; + } + + virtual int GetScrollIncrement(views::ScrollBar* source, + bool is_page, + bool is_positive) OVERRIDE { + last_source = source; + last_is_page = is_page; + last_is_positive = is_positive; + + if (is_page) + return 20; + return 10; + } + + // We save the last values in order to assert the corectness of the scroll + // operation. + views::ScrollBar* last_source; + bool last_is_positive; + bool last_is_page; + int last_position; +}; + +} // namespace + +namespace views { + +class NativeScrollBarTest : public ViewsTestBase { + public: + NativeScrollBarTest() + : widget_(NULL), + scrollbar_(NULL), + controller_(NULL) { + } + + virtual void SetUp() { + ViewsTestBase::SetUp(); + Widget::SetPureViews(true); + } + + virtual void TearDown() { + Widget::SetPureViews(false); + if (widget_) + widget_->Close(); + ViewsTestBase::TearDown(); + } + + void InitScrollBar() { + controller_.reset(new TestScrollBarController()); + + ASSERT_FALSE(scrollbar_); + native_scrollbar_ = new NativeScrollBar(true); + native_scrollbar_->SetBounds(0, 0, 100, 100); + scrollbar_ = new NativeScrollBarViews(native_scrollbar_); + scrollbar_->SetController(controller_.get()); + + widget_ = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.bounds = gfx::Rect(0, 0, 100, 100); + widget_->Init(params); + View* container = new View(); + widget_->SetContentsView(container); + container->AddChildView(scrollbar_); + scrollbar_->SetBounds(0, 0, 100, 100); + scrollbar_->Update(100, 200, 0); + + track_size_ = scrollbar_->GetTrackBounds().width(); + } + + protected: + Widget* widget_; + + // This is the native scrollbar the Views one wraps around. + NativeScrollBar* native_scrollbar_; + + // This is the Views scrollbar. + BaseScrollBar* scrollbar_; + + // Keep track of the size of the track. This is how we can tell when we + // scroll to the middle. + int track_size_; + + scoped_ptr<TestScrollBarController> controller_; +}; + +// TODO(dnicoara) Can't run the test on Windows since the scrollbar |Part| +// isn't handled in NativeTheme. +#if defined(OS_WIN) +TEST_F(NativeScrollBarTest, DISABLED_Scrolling) { +#else +TEST_F(NativeScrollBarTest, Scrolling) { +#endif + InitScrollBar(); + EXPECT_EQ(scrollbar_->GetPosition(), 0); + EXPECT_EQ(scrollbar_->GetMaxPosition(), 100); + EXPECT_EQ(scrollbar_->GetMinPosition(), 0); + + // Scroll to middle. + scrollbar_->ScrollToThumbPosition(track_size_ / 4, false); + EXPECT_EQ(controller_->last_position, 50); + EXPECT_EQ(controller_->last_source, native_scrollbar_); + + // Scroll to the end. + scrollbar_->ScrollToThumbPosition(track_size_ / 2, false); + EXPECT_EQ(controller_->last_position, 100); + + // Overscroll. Last position should be the maximum position. + scrollbar_->ScrollToThumbPosition(track_size_, false); + EXPECT_EQ(controller_->last_position, 100); + + // Underscroll. Last position should be the minimum position. + scrollbar_->ScrollToThumbPosition(-10, false); + EXPECT_EQ(controller_->last_position, 0); + + // Test the different fixed scrolling amounts. Generally used by buttons, + // or click on track. + scrollbar_->ScrollToThumbPosition(0, false); + scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_NEXT_LINE); + EXPECT_EQ(controller_->last_position, 10); + + scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_PREV_LINE); + EXPECT_EQ(controller_->last_position, 0); + + scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_NEXT_PAGE); + EXPECT_EQ(controller_->last_position, 20); + + scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_PREV_PAGE); + EXPECT_EQ(controller_->last_position, 0); +} + +} // namespace views diff --git a/views/views.gyp b/views/views.gyp index 8415705..f770895 100644 --- a/views/views.gyp +++ b/views/views.gyp @@ -171,10 +171,18 @@ 'controls/resize_area.h', 'controls/scroll_view.cc', 'controls/scroll_view.h', + 'controls/scrollbar/base_scroll_bar.cc', + 'controls/scrollbar/base_scroll_bar.h', + 'controls/scrollbar/base_scroll_bar_button.cc', + 'controls/scrollbar/base_scroll_bar_button.h', + 'controls/scrollbar/base_scroll_bar_thumb.cc', + 'controls/scrollbar/base_scroll_bar_thumb.h', 'controls/scrollbar/bitmap_scroll_bar.cc', 'controls/scrollbar/bitmap_scroll_bar.h', 'controls/scrollbar/native_scroll_bar_gtk.cc', 'controls/scrollbar/native_scroll_bar_gtk.h', + 'controls/scrollbar/native_scroll_bar_views.cc', + 'controls/scrollbar/native_scroll_bar_views.h', 'controls/scrollbar/native_scroll_bar_win.cc', 'controls/scrollbar/native_scroll_bar_win.h', 'controls/scrollbar/native_scroll_bar_wrapper.h', @@ -490,6 +498,7 @@ 'controls/menu/menu_model_adapter_unittest.cc', 'controls/textfield/native_textfield_views_unittest.cc', 'controls/textfield/textfield_views_model_unittest.cc', + 'controls/scrollbar/scrollbar_unittest.cc', 'events/event_unittest.cc', 'focus/accelerator_handler_gtk_unittest.cc', 'focus/focus_manager_unittest.cc', |