summaryrefslogtreecommitdiffstats
path: root/chrome/views/scroll_view.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/views/scroll_view.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/views/scroll_view.cc')
-rw-r--r--chrome/views/scroll_view.cc534
1 files changed, 534 insertions, 0 deletions
diff --git a/chrome/views/scroll_view.cc b/chrome/views/scroll_view.cc
new file mode 100644
index 0000000..a7b59b1
--- /dev/null
+++ b/chrome/views/scroll_view.cc
@@ -0,0 +1,534 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/views/scroll_view.h"
+
+#include "base/logging.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/native_scroll_bar.h"
+#include "chrome/views/root_view.h"
+
+namespace ChromeViews {
+
+const char* const ScrollView::kViewClassName = "chrome/views/ScrollView";
+
+// Viewport contains the contents View of the ScrollView.
+class Viewport : public View {
+ public:
+ Viewport() {}
+ virtual ~Viewport() {}
+
+ virtual void ScrollRectToVisible(int x, int y, int width, int height) {
+ if (!GetChildViewCount() || !GetParent())
+ return;
+
+ View* contents = GetChildViewAt(0);
+ x -= contents->GetX();
+ y -= contents->GetY();
+ static_cast<ScrollView*>(GetParent())->ScrollContentsRegionToBeVisible(
+ x, y, width, height);
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(Viewport);
+};
+
+
+ScrollView::ScrollView() {
+ Init(new NativeScrollBar(true), new NativeScrollBar(false), NULL);
+}
+
+ScrollView::ScrollView(ScrollBar* horizontal_scrollbar,
+ ScrollBar* vertical_scrollbar,
+ View* resize_corner) {
+ Init(horizontal_scrollbar, vertical_scrollbar, resize_corner);
+}
+
+ScrollView::~ScrollView() {
+ // If scrollbars are currently not used, delete them
+ if (!horiz_sb_->GetParent()) {
+ delete horiz_sb_;
+ }
+
+ if (!vert_sb_->GetParent()) {
+ delete vert_sb_;
+ }
+
+ if (resize_corner_ && !resize_corner_->GetParent()) {
+ delete resize_corner_;
+ }
+}
+
+void ScrollView::SetContents(View* a_view) {
+ if (contents_ && contents_ != a_view) {
+ viewport_->RemoveChildView(contents_);
+ delete contents_;
+ contents_ = NULL;
+ }
+
+ if (a_view) {
+ contents_ = a_view;
+ viewport_->AddChildView(contents_);
+ }
+
+ Layout();
+}
+
+View* ScrollView::GetContents() const {
+ return contents_;
+}
+
+void ScrollView::Init(ScrollBar* horizontal_scrollbar,
+ ScrollBar* vertical_scrollbar,
+ View* resize_corner) {
+ DCHECK(horizontal_scrollbar && vertical_scrollbar);
+
+ contents_ = NULL;
+ horiz_sb_ = horizontal_scrollbar;
+ vert_sb_ = vertical_scrollbar;
+ resize_corner_ = resize_corner;
+
+ viewport_ = new Viewport();
+ AddChildView(viewport_);
+
+ // Don't add the scrollbars as children until we discover we need them
+ // (ShowOrHideScrollBar).
+ horiz_sb_->SetVisible(false);
+ horiz_sb_->SetController(this);
+ vert_sb_->SetVisible(false);
+ vert_sb_->SetController(this);
+ if (resize_corner_)
+ resize_corner_->SetVisible(false);
+}
+
+// Make sure that a single scrollbar is created and visible as needed
+void ScrollView::SetControlVisibility(View* control, bool should_show) {
+ if (!control)
+ return;
+ if (should_show) {
+ if (!control->IsVisible()) {
+ AddChildView(control);
+ control->SetVisible(true);
+ }
+ } else {
+ RemoveChildView(control);
+ control->SetVisible(false);
+ }
+}
+
+void ScrollView::ComputeScrollBarsVisibility(const CSize& vp_size,
+ const CSize& content_size,
+ bool* horiz_is_shown,
+ bool* vert_is_shown) const {
+ // Try to fit both ways first, then try vertical bar only, then horizontal
+ // bar only, then defaults to both shown.
+ if (content_size.cx <= vp_size.cx && content_size.cy <= vp_size.cy) {
+ *horiz_is_shown = false;
+ *vert_is_shown = false;
+ } else if (content_size.cx <= vp_size.cx - GetScrollBarWidth()) {
+ *horiz_is_shown = false;
+ *vert_is_shown = true;
+ } else if (content_size.cy <= vp_size.cy - GetScrollBarHeight()) {
+ *horiz_is_shown = true;
+ *vert_is_shown = false;
+ } else {
+ *horiz_is_shown = true;
+ *vert_is_shown = true;
+ }
+}
+
+void ScrollView::Layout() {
+ // Most views will want to auto-fit the available space. Most of them want to
+ // use the all available width (without overflowing) and only overflow in
+ // height. Examples are HistoryView, MostVisitedView, DownloadTabView, etc.
+ // Other views want to fit in both ways. An example is PrintView. To make both
+ // happy, assume a vertical scrollbar but no horizontal scrollbar. To
+ // override this default behavior, the inner view has to calculate the
+ // available space, used ComputeScrollBarsVisibility() to use the same
+ // calculation that is done here and sets its bound to fit within.
+ WTL::CRect viewport_bounds;
+ GetLocalBounds(&viewport_bounds, true);
+ // Realign it to 0 so it can be used as-is for SetBounds().
+ viewport_bounds.MoveToXY(0, 0);
+ // viewport_size is the total client space available.
+ WTL::CSize viewport_size(viewport_bounds.right,
+ viewport_bounds.bottom);
+ if (viewport_size.cx == 0 || viewport_size.cy == 0) {
+ // There's nothing to layout.
+ return;
+ }
+
+ // Assumes a vertical scrollbar since most the current views are designed for
+ // this.
+ int horiz_sb_height = GetScrollBarHeight();
+ int vert_sb_width = GetScrollBarWidth();
+ viewport_bounds.right -= vert_sb_width;
+ // Update the bounds right now so the inner views can fit in it.
+ viewport_->SetBounds(viewport_bounds);
+
+ // Give contents_ a chance to update its bounds if it depends on the viewport.
+ if (contents_) {
+ contents_->Layout();
+ }
+
+ bool should_layout_contents = false;
+ bool horiz_sb_required = false;
+ bool vert_sb_required = false;
+ if (contents_) {
+ WTL::CSize content_size(contents_->GetWidth(), contents_->GetHeight());
+ ComputeScrollBarsVisibility(viewport_size,
+ content_size,
+ &horiz_sb_required,
+ &vert_sb_required);
+ }
+ bool resize_corner_required = resize_corner_ && horiz_sb_required &&
+ vert_sb_required;
+ // Take action.
+ SetControlVisibility(horiz_sb_, horiz_sb_required);
+ SetControlVisibility(vert_sb_, vert_sb_required);
+ SetControlVisibility(resize_corner_, resize_corner_required);
+
+ // Non-default.
+ if (horiz_sb_required) {
+ viewport_bounds.bottom -= horiz_sb_height;
+ should_layout_contents = true;
+ }
+ // Default.
+ if (!vert_sb_required) {
+ viewport_bounds.right += vert_sb_width;
+ should_layout_contents = true;
+ }
+
+ if (horiz_sb_required) {
+ horiz_sb_->SetBounds(0,
+ viewport_bounds.bottom,
+ viewport_bounds.right,
+ horiz_sb_height);
+ }
+ if (vert_sb_required) {
+ vert_sb_->SetBounds(viewport_bounds.right,
+ 0,
+ vert_sb_width,
+ viewport_bounds.bottom);
+ }
+ if (resize_corner_required) {
+ // Show the resize corner.
+ resize_corner_->SetBounds(viewport_bounds.right,
+ viewport_bounds.bottom,
+ vert_sb_width,
+ horiz_sb_height);
+ }
+
+ // Update to the real client size with the visible scrollbars.
+ viewport_->SetBounds(viewport_bounds);
+ if (should_layout_contents && contents_)
+ contents_->Layout();
+
+ CheckScrollBounds();
+ SchedulePaint();
+ UpdateScrollBarPositions();
+}
+
+int ScrollView::CheckScrollBounds(int viewport_size,
+ int content_size,
+ int current_pos) {
+ int max = std::max(content_size - viewport_size, 0);
+ if (current_pos < 0)
+ current_pos = 0;
+ else if (current_pos > max)
+ current_pos = max;
+ return current_pos;
+}
+
+void ScrollView::CheckScrollBounds() {
+ if (contents_) {
+ int x, y;
+
+ x = CheckScrollBounds(viewport_->GetWidth(),
+ contents_->GetWidth(),
+ -contents_->GetX());
+ y = CheckScrollBounds(viewport_->GetHeight(),
+ contents_->GetHeight(),
+ -contents_->GetY());
+
+ // This is no op if bounds are the same
+ contents_->SetBounds(-x, -y, contents_->GetWidth(), contents_->GetHeight());
+ }
+}
+
+void ScrollView::DidChangeBounds(const CRect& previous, const CRect& current) {
+ Layout();
+}
+
+gfx::Rect ScrollView::GetVisibleRect() const {
+ if (!contents_)
+ return gfx::Rect();
+
+ const int x =
+ (horiz_sb_ && horiz_sb_->IsVisible()) ? horiz_sb_->GetPosition() : 0;
+ const int y =
+ (vert_sb_ && vert_sb_->IsVisible()) ? vert_sb_->GetPosition() : 0;
+ return gfx::Rect(x, y, viewport_->GetWidth(), viewport_->GetHeight());
+}
+
+void ScrollView::ScrollContentsRegionToBeVisible(int x,
+ int y,
+ int width,
+ int height) {
+ if (!contents_ || ((!horiz_sb_ || !horiz_sb_->IsVisible()) &&
+ (!vert_sb_ || !vert_sb_->IsVisible()))) {
+ return;
+ }
+
+ const int contents_max_x =
+ std::max(viewport_->GetWidth(), contents_->GetWidth());
+ const int contents_max_y =
+ std::max(viewport_->GetHeight(), contents_->GetHeight());
+ x = std::max(0, std::min(contents_max_x, x));
+ y = std::max(0, std::min(contents_max_y, y));
+ const int max_x = std::min(contents_max_x,
+ x + std::min(width, viewport_->GetWidth()));
+ const int max_y = std::min(contents_max_y,
+ y + std::min(height,
+ viewport_->GetHeight()));
+ const gfx::Rect vis_rect = GetVisibleRect();
+ if (vis_rect.Contains(gfx::Rect(x, y, max_x, max_y)))
+ return;
+
+ const int new_x =
+ (vis_rect.x() < x) ? x : std::max(0, max_x - viewport_->GetWidth());
+ const int new_y =
+ (vis_rect.y() < y) ? y : std::max(0, max_x - viewport_->GetHeight());
+
+ contents_->SetX(-new_x);
+ contents_->SetY(-new_y);
+ UpdateScrollBarPositions();
+}
+
+void ScrollView::UpdateScrollBarPositions() {
+ if (!contents_) {
+ return;
+ }
+
+ if (horiz_sb_->IsVisible()) {
+ int vw = viewport_->GetWidth();
+ int cw = contents_->GetWidth();
+ int origin = contents_->GetX();
+ horiz_sb_->Update(vw, cw, -origin);
+ }
+ if (vert_sb_->IsVisible()) {
+ int vh = viewport_->GetHeight();
+ int ch = contents_->GetHeight();
+ int origin = contents_->GetY();
+ vert_sb_->Update(vh, ch, -origin);
+ }
+}
+
+// TODO(ACW). We should really use ScrollWindowEx as needed
+void ScrollView::ScrollToPosition(ScrollBar* source, int position) {
+ if (!contents_)
+ return;
+
+ if (source == horiz_sb_ && horiz_sb_->IsVisible()) {
+ int vw = viewport_->GetWidth();
+ int cw = contents_->GetWidth();
+ int origin = contents_->GetX();
+ if (-origin != position) {
+ int max_pos = std::max(0, cw - vw);
+ if (position < 0)
+ position = 0;
+ else if (position > max_pos)
+ position = max_pos;
+ contents_->SetX(-position);
+ CRect bounds;
+ contents_->GetLocalBounds(&bounds, true);
+ contents_->SchedulePaint(bounds, true);
+ }
+ } else if (source == vert_sb_ && vert_sb_->IsVisible()) {
+ int vh = viewport_->GetHeight();
+ int ch = contents_->GetHeight();
+ int origin = contents_->GetY();
+ if (-origin != position) {
+ int max_pos = std::max(0, ch - vh);
+ if (position < 0)
+ position = 0;
+ else if (position > max_pos)
+ position = max_pos;
+ contents_->SetY(-position);
+ CRect bounds;
+ contents_->GetLocalBounds(&bounds, true);
+ contents_->SchedulePaint(bounds, true);
+ }
+ }
+}
+
+int ScrollView::GetScrollIncrement(ScrollBar* source, bool is_page,
+ bool is_positive) {
+ bool is_horizontal = source->IsHorizontal();
+ int amount = 0;
+ View* view = GetContents();
+ if (view) {
+ if (is_page)
+ amount = view->GetPageScrollIncrement(this, is_horizontal, is_positive);
+ else
+ amount = view->GetLineScrollIncrement(this, is_horizontal, is_positive);
+ if (amount > 0)
+ return amount;
+ }
+ // No view, or the view didn't return a valid amount.
+ if (is_page)
+ return is_horizontal ? viewport_->GetWidth() : viewport_->GetHeight();
+ return is_horizontal ? viewport_->GetWidth() / 5 : viewport_->GetHeight() / 5;
+}
+
+void ScrollView::ViewHierarchyChanged(bool is_add, View *parent, View *child) {
+ if (is_add) {
+ RootView* rv = GetRootView();
+ if (rv) {
+ rv->SetDefaultKeyboardHandler(this);
+ rv->SetFocusOnMousePressed(true);
+ }
+ }
+}
+
+bool ScrollView::OnKeyPressed(const KeyEvent& event) {
+ bool processed = false;
+
+ // Give vertical scrollbar priority
+ if (vert_sb_->IsVisible()) {
+ processed = vert_sb_->OnKeyPressed(event);
+ }
+
+ if (!processed && horiz_sb_->IsVisible()) {
+ processed = horiz_sb_->OnKeyPressed(event);
+ }
+ return processed;
+}
+
+bool ScrollView::OnMouseWheel(const MouseWheelEvent& e) {
+ bool processed = false;
+
+ // Give vertical scrollbar priority
+ if (vert_sb_->IsVisible()) {
+ processed = vert_sb_->OnMouseWheel(e);
+ }
+
+ if (!processed && horiz_sb_->IsVisible()) {
+ processed = horiz_sb_->OnMouseWheel(e);
+ }
+ return processed;
+}
+
+std::string ScrollView::GetClassName() const {
+ return kViewClassName;
+}
+
+int ScrollView::GetScrollBarWidth() const {
+ return vert_sb_->GetLayoutSize();
+}
+
+int ScrollView::GetScrollBarHeight() const {
+ return horiz_sb_->GetLayoutSize();
+}
+
+// VariableRowHeightScrollHelper ----------------------------------------------
+
+VariableRowHeightScrollHelper::VariableRowHeightScrollHelper(
+ Controller* controller) : controller_(controller) {
+}
+
+VariableRowHeightScrollHelper::~VariableRowHeightScrollHelper() {
+}
+
+int VariableRowHeightScrollHelper::GetPageScrollIncrement(
+ ScrollView* scroll_view, bool is_horizontal, bool is_positive) {
+ if (is_horizontal)
+ return 0;
+ // y coordinate is most likely negative.
+ int y = abs(scroll_view->GetContents()->GetY());
+ int vis_height = scroll_view->GetContents()->GetParent()->GetHeight();
+ if (is_positive) {
+ // Align the bottom most row to the top of the view.
+ int bottom = std::min(scroll_view->GetContents()->GetHeight() - 1,
+ y + vis_height);
+ RowInfo bottom_row_info = GetRowInfo(bottom);
+ // If 0, ScrollView will provide a default value.
+ return std::max(0, bottom_row_info.origin - y);
+ } else {
+ // Align the row on the previous page to to the top of the view.
+ int last_page_y = y - vis_height;
+ RowInfo last_page_info = GetRowInfo(std::max(0, last_page_y));
+ if (last_page_y != last_page_info.origin)
+ return std::max(0, y - last_page_info.origin - last_page_info.height);
+ return std::max(0, y - last_page_info.origin);
+ }
+}
+
+int VariableRowHeightScrollHelper::GetLineScrollIncrement(
+ ScrollView* scroll_view, bool is_horizontal, bool is_positive) {
+ if (is_horizontal)
+ return 0;
+ // y coordinate is most likely negative.
+ int y = abs(scroll_view->GetContents()->GetY());
+ RowInfo row = GetRowInfo(y);
+ if (is_positive) {
+ return row.height - (y - row.origin);
+ } else if (y == row.origin) {
+ row = GetRowInfo(std::max(0, row.origin - 1));
+ return y - row.origin;
+ } else {
+ return y - row.origin;
+ }
+}
+
+VariableRowHeightScrollHelper::RowInfo
+ VariableRowHeightScrollHelper::GetRowInfo(int y) {
+ return controller_->GetRowInfo(y);
+}
+
+// FixedRowHeightScrollHelper -----------------------------------------------
+
+FixedRowHeightScrollHelper::FixedRowHeightScrollHelper(int top_margin,
+ int row_height)
+ : VariableRowHeightScrollHelper(NULL),
+ top_margin_(top_margin),
+ row_height_(row_height) {
+ DCHECK(row_height > 0);
+}
+
+VariableRowHeightScrollHelper::RowInfo
+ FixedRowHeightScrollHelper::GetRowInfo(int y) {
+ if (y < top_margin_)
+ return RowInfo(0, top_margin_);
+ return RowInfo((y - top_margin_) / row_height_ * row_height_ + top_margin_,
+ row_height_);
+}
+
+}