Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(921)

Unified Diff: views/controls/scrollbar/native_scroll_bar_views.cc

Issue 7669028: Adding a Views scrollbar implementation. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Fixed keyboard codes include Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: views/controls/scrollbar/native_scroll_bar_views.cc
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 0000000000000000000000000000000000000000..be1fce50f2774c9bb1aa8b25ec5ef1d9300db618
--- /dev/null
+++ b/views/controls/scrollbar/native_scroll_bar_views.cc
@@ -0,0 +1,571 @@
+// 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"
+
+namespace views {
+
+namespace {
+
+// Wrapper for the scroll buttons.
+class ScrollBarButton : public CustomButton {
Ben Goodger (Google) 2011/08/19 17:44:33 Rather than reinvent the wheel here I'd rather you
+ public:
+ enum Type {
+ UP,
+ DOWN,
+ LEFT,
+ RIGHT,
+ };
+
+ ScrollBarButton(ButtonListener* listener, Type type);
+ virtual ~ScrollBarButton();
+
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+
+ protected:
+ virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseEntered(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseExited(const MouseEvent& event) OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+ private:
+ gfx::NativeTheme::ExtraParams params_;
+ gfx::NativeTheme::Part part_;
+ gfx::NativeTheme::State state_;
+};
+
+} // namespace
+
+// Wrapper for the scroll thumb
+class ScrollBarThumb : public View {
+ public:
+ enum Type {
+ VERTICAL,
+ HORIZONTAL,
+ };
+
+ ScrollBarThumb(NativeScrollBarWrapper* wrapper, Type type);
+ virtual ~ScrollBarThumb();
+
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+
+ void Update(int position);
+ void Update(int viewport_size, int content_size, int position);
+
+ int current_position() const { return current_position_; }
+
+ // This is called when the layout changes and the scrollbar changes
+ // size.
+ void set_track_bounds(const gfx::Rect& bounds) { track_bounds_ = bounds; }
+
+ protected:
+ virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE;
+ virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseEntered(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseExited(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseMoved(const MouseEvent& event) OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+ private:
+ // Converts the position of the thumb to the pixel size of the thumb
+ // relative to the size of the drawing area.
+ int GetThumbPosition(int drawing_size) const;
+
+ // Returns the size of the thumb relative to the drawing area size.
+ int GetThumbSize(int drawing_size) const;
+
+ NativeScrollBarWrapper* wrapper_;
+
+ Type type_;
+
+ // The current position of the thumb.
+ int current_position_;
+
+ // The size of the content we want to display.
+ int content_size_;
+ // The size of the viewport (displayable area).
+ int viewport_size_;
+
+ // The track bounds. We need this to compute the size of the thumb relative
+ // to the size of the track.
+ gfx::Rect track_bounds_;
+
+ // The distance from the tip of the thumb. Used when dragging to keep the
+ // position of the thumb at the same relative distance from the mouse click
+ // event.
+ int delta_;
+
+ // Parameters required for the native theme. We keep these here since
+ // we may change them based on mouse events (hover, click, ...).
+ gfx::NativeTheme::ExtraParams params_;
+ gfx::NativeTheme::Part part_;
+ gfx::NativeTheme::State state_;
+};
+
+namespace {
+
+/////////////////////////////////////////////////////////////////////////////
+// ScrollBarButton
+
+ScrollBarButton::ScrollBarButton(ButtonListener* listener, Type type)
+ : CustomButton(listener),
+ params_(),
+ state_(gfx::NativeTheme::kNormal) {
+ switch (type) {
+ case UP:
+ part_ = gfx::NativeTheme::kScrollbarUpArrow;
+ break;
+ case DOWN:
+ part_ = gfx::NativeTheme::kScrollbarDownArrow;
+ break;
+ case LEFT:
+ part_ = gfx::NativeTheme::kScrollbarLeftArrow;
+ break;
+ case RIGHT:
+ part_ = gfx::NativeTheme::kScrollbarRightArrow;
+ break;
+ }
+
+ params_.scrollbar_arrow.is_hovering = false;
+}
+
+ScrollBarButton::~ScrollBarButton() {
+}
+
+gfx::Size ScrollBarButton::GetPreferredSize() {
+ const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance();
+ return native_theme->GetPartSize(part_, state_, params_);
+}
+
+bool ScrollBarButton::OnMousePressed(const MouseEvent& event) {
+ if (event.IsOnlyLeftMouseButton())
+ Button::NotifyClick(event);
+ return true;
+}
+
+void ScrollBarButton::OnMouseReleased(const MouseEvent& event) {
+}
+
+void ScrollBarButton::OnMouseEntered(const MouseEvent& event) {
+ state_ = gfx::NativeTheme::kHovered;
+ params_.scrollbar_arrow.is_hovering = true;
+ SchedulePaint();
+}
+
+void ScrollBarButton::OnMouseExited(const MouseEvent& event) {
+ state_ = gfx::NativeTheme::kNormal;
+ params_.scrollbar_arrow.is_hovering = false;
+ SchedulePaint();
+}
+
+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(),
+ part_,
+ state_,
+ bounds,
+ params_);
+}
+
+} // namespace
+
+/////////////////////////////////////////////////////////////////////////////
+// ScrollBarThumb
+
+ScrollBarThumb::ScrollBarThumb(NativeScrollBarWrapper* wrapper, Type type)
+ : wrapper_(wrapper),
+ type_(type),
+ state_(gfx::NativeTheme::kNormal) {
+ switch (type) {
+ case VERTICAL:
+ part_ = gfx::NativeTheme::kScrollbarVerticalThumb;
+ break;
+ case HORIZONTAL:
+ part_ = gfx::NativeTheme::kScrollbarHorizontalThumb;
+ break;
+ }
+ params_.scrollbar_thumb.is_hovering = false;
+}
+
+ScrollBarThumb::~ScrollBarThumb() {
+}
+
+gfx::Size ScrollBarThumb::GetPreferredSize() {
+ const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance();
+ return native_theme->GetPartSize(part_, state_, params_);
+}
+
+void ScrollBarThumb::Update(int position) {
+ Update(viewport_size_, content_size_, position);
+}
+
+void ScrollBarThumb::Update(int viewport_size,
+ int content_size,
+ int position) {
+ // First constrain the size and position to the actual values used by the
+ // scrollbar.
+ if (content_size < 0)
+ content_size = 0;
+
+ if (position < 0)
+ position = 0;
+
+ if (position > content_size - viewport_size)
+ position = content_size - viewport_size;
+
+ current_position_ = position;
+ content_size_ = content_size;
+ viewport_size_ = viewport_size;
+
+ gfx::Rect bounds = GetLocalBounds();
+
+ if (type_ == HORIZONTAL) {
+ bounds.set_x(track_bounds_.x() +
+ GetThumbPosition(track_bounds_.width()));
+ bounds.set_width(GetThumbSize(track_bounds_.width()));
+ } else {
+ bounds.set_y(track_bounds_.y() +
+ GetThumbPosition(track_bounds_.height()));
+ bounds.set_height(GetThumbSize(track_bounds_.height()));
+ }
+
+ // Update the position and size of the thumb.
+ SetBoundsRect(bounds);
+
+ SchedulePaint();
+}
+
+bool ScrollBarThumb::OnMousePressed(const MouseEvent& event) {
+ if (!event.IsOnlyLeftMouseButton())
+ return false;
+
+ // Keep the distance from the tip of the thumb so that scrolling movement
+ // looks nice. (We want events relative to the mouse click.)
+ if (type_ == HORIZONTAL) {
+ int track_size = track_bounds_.width();
+ delta_ = -event.x() * content_size_ / track_size;
+ } else {
+ int track_size = track_bounds_.height();
+ delta_ = -event.y() * content_size_ / track_size;
+ }
+
+ return true;
+}
+
+
+bool ScrollBarThumb::OnMouseDragged(const MouseEvent& event) {
+ // Let the scrollbar wrapper know that we're changing the current position.
+ // It should then all for UI updates.
+ if (type_ == HORIZONTAL) {
+ int track_size = track_bounds_.width();
+ wrapper_->Update(viewport_size_,
+ content_size_,
+ current_position_ + delta_ +
+ event.x() * content_size_ / track_size);
+ } else {
+ int track_size = track_bounds_.height();
+ wrapper_->Update(viewport_size_,
+ content_size_,
+ current_position_ + delta_ +
+ event.y() * content_size_ / track_size);
+ }
+
+ return true;
+}
+
+void ScrollBarThumb::OnMouseEntered(const MouseEvent& event) {
+ if (state_ != gfx::NativeTheme::kHovered) {
+ state_ = gfx::NativeTheme::kHovered;
+ params_.scrollbar_thumb.is_hovering = true;
+ SchedulePaint();
+ }
+}
+
+void ScrollBarThumb::OnMouseExited(const MouseEvent& event) {
+ if (state_ != gfx::NativeTheme::kNormal) {
+ state_ = gfx::NativeTheme::kNormal;
+ params_.scrollbar_thumb.is_hovering = false;
+ SchedulePaint();
+ }
+}
+
+void ScrollBarThumb::OnMouseMoved(const MouseEvent& event) {
+ OnMouseEntered(event);
+}
+
+void ScrollBarThumb::OnPaint(gfx::Canvas* canvas) {
+ const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance();
+
+ native_theme->Paint(canvas->AsCanvasSkia(),
+ part_,
+ state_,
+ GetLocalBounds(),
+ params_);
+}
+
+int ScrollBarThumb::GetThumbSize(int drawing_size) const {
+ return (viewport_size_ * drawing_size) /
+ (content_size_ ? content_size_ : 1);
+}
+
+int ScrollBarThumb::GetThumbPosition(int drawing_size) const {
+ return (current_position_ * drawing_size) /
+ (content_size_ ? content_size_ : 1);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeScrollBarViews, public:
+
+NativeScrollBarViews::NativeScrollBarViews(NativeScrollBar* scroll_bar)
+ : native_scroll_bar_(scroll_bar) {
+ if (native_scroll_bar_->IsHorizontal()) {
+ prev_button_ = new ScrollBarButton(this, ScrollBarButton::LEFT);
+ next_button_ = new ScrollBarButton(this, ScrollBarButton::RIGHT);
+ thumb_ = new ScrollBarThumb(this, ScrollBarThumb::HORIZONTAL);
+
+ part_ = gfx::NativeTheme::kScrollbarHorizontalTrack;
+ } else {
+ prev_button_ = new ScrollBarButton(this, ScrollBarButton::UP);
+ next_button_ = new ScrollBarButton(this, ScrollBarButton::DOWN);
+ thumb_ = new ScrollBarThumb(this, ScrollBarThumb::VERTICAL);
+
+ part_ = gfx::NativeTheme::kScrollbarVerticalTrack;
+ }
+
+ state_ = gfx::NativeTheme::kNormal;
+
+ AddChildView(prev_button_);
+ AddChildView(next_button_);
+ AddChildView(thumb_);
+}
+
+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());
+ }
+
+ // Update the size of the thumb and tell it that the track size also changed.
+ thumb_->set_track_bounds(GetTrackBounds());
+ thumb_->SetBoundsRect(GetTrackBounds());
+}
+
+gfx::Size NativeScrollBarViews::GetPreferredSize() {
+ if (native_scroll_bar_->IsHorizontal())
+ return gfx::Size(0, GetHorizontalScrollBarHeight());
+ return gfx::Size(GetVerticalScrollBarWidth(), 0);
+}
+
+// TODO(oshima|jcampan): key/mouse events are not delievered and
+// the following code is not tested. It requires the focus manager to be fully
+// implemented.
+bool NativeScrollBarViews::OnKeyPressed(const KeyEvent& event) {
+ switch (event.key_code()) {
+ case ui::VKEY_UP:
+ if (!native_scroll_bar_->IsHorizontal())
+ MoveStep(false /* negative */);
+ break;
+ case ui::VKEY_DOWN:
+ if (!native_scroll_bar_->IsHorizontal())
+ MoveStep(true /* positive */);
+ break;
+ case ui::VKEY_LEFT:
+ if (native_scroll_bar_->IsHorizontal())
+ MoveStep(false /* negative */);
+ break;
+ case ui::VKEY_RIGHT:
+ if (native_scroll_bar_->IsHorizontal())
+ MoveStep(true /* positive */);
+ break;
+ case ui::VKEY_PRIOR:
+ MovePage(false /* negative */);
+ break;
+ case ui::VKEY_NEXT:
+ MovePage(true /* positive */);
+ break;
+ case ui::VKEY_HOME:
+ MoveTo(0);
+ break;
+ case ui::VKEY_END:
+ MoveToBottom();
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool NativeScrollBarViews::OnMousePressed(const MouseEvent& event) {
+ if (!event.IsOnlyLeftMouseButton())
+ return true;
+
+ if (native_scroll_bar_->IsHorizontal()) {
+ if (event.x() < thumb_->bounds().x())
+ MovePage(false);
+ else
+ MovePage(true);
+ } else {
+ if (event.y() < thumb_->bounds().y())
+ MovePage(false);
+ else
+ MovePage(true);
+ }
+
+ return true;
+}
+
+void NativeScrollBarViews::OnMouseEntered(const MouseEvent& event) {
+ state_ = gfx::NativeTheme::kHovered;
+ SchedulePaint();
+}
+
+void NativeScrollBarViews::OnMouseExited(const MouseEvent& event) {
+ state_ = gfx::NativeTheme::kNormal;
+ SchedulePaint();
+}
+
+bool NativeScrollBarViews::OnMouseWheel(const MouseWheelEvent& e) {
+ MoveBy(-e.offset()); // e.GetOffset() > 0 means scroll up.
+ return true;
+}
+
+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_);
+}
+
+void NativeScrollBarViews::ButtonPressed(Button* sender,
+ const views::Event& event) {
+ if (sender == prev_button_) {
+ MoveStep(false);
+ } else if (sender == next_button_) {
+ MoveStep(true);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeScrollBarViews, NativeScrollBarWrapper overrides:
+
+int NativeScrollBarViews::GetPosition() const {
+ return thumb_->current_position();
+}
+
+View* NativeScrollBarViews::GetView() {
+ return this;
+}
+
+void NativeScrollBarViews::Update(int viewport_size,
+ int content_size,
+ int current_pos) {
+ thumb_->Update(viewport_size, content_size, current_pos);
+
+ ValueChanged();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeScrollBarViews, private:
+
+void NativeScrollBarViews::ValueChanged() {
+ ScrollBarController* controller = native_scroll_bar_->GetController();
+ controller->ScrollToPosition(native_scroll_bar_, GetPosition());
+}
+
+void NativeScrollBarViews::MoveBy(int o) {
+ MoveTo(GetPosition() + o);
+}
+
+void NativeScrollBarViews::MovePage(bool positive) {
+ ScrollBarController* controller = native_scroll_bar_->GetController();
+ int scroll_increment = controller->GetScrollIncrement(native_scroll_bar_,
+ true,
+ positive);
+ if (!positive)
+ scroll_increment *= -1;
+
+ MoveBy(scroll_increment);
+}
+
+void NativeScrollBarViews::MoveStep(bool positive) {
+ ScrollBarController* controller = native_scroll_bar_->GetController();
+ int scroll_increment = controller->GetScrollIncrement(native_scroll_bar_,
+ false,
+ positive);
+ if (!positive)
+ scroll_increment *= -1;
+
+ MoveBy(scroll_increment);
+}
+
+void NativeScrollBarViews::MoveTo(int p) {
+ if (p < native_scroll_bar_->GetMinPosition())
+ p = native_scroll_bar_->GetMinPosition();
+ if (p > native_scroll_bar_->GetMaxPosition())
+ p = native_scroll_bar_->GetMaxPosition();
+
+ thumb_->Update(p);
+ ValueChanged();
+}
+
+void NativeScrollBarViews::MoveToBottom() {
+ MoveTo(native_scroll_bar_->GetMaxPosition());
+}
+
+gfx::Rect NativeScrollBarViews::GetTrackBounds() const {
+ gfx::Rect bounds = GetLocalBounds();
+ gfx::Size size = prev_button_->GetPreferredSize();
+
+ 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

Powered by Google App Engine
This is Rietveld 408576698