15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/scroll_view.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
8d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event.h"
96e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "ui/gfx/canvas.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/native_theme/native_theme.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/border.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/scrollbar/native_scroll_bar.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/root_view.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace views {
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char ScrollView::kViewClassName[] = "ScrollView";
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Subclass of ScrollView that resets the border when the theme changes.
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ScrollViewWithBorder : public views::ScrollView {
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  ScrollViewWithBorder() {}
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // View overrides;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE {
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SetBorder(Border::CreateSolidBorder(
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        1,
305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        theme->GetSystemColor(ui::NativeTheme::kColorId_UnfocusedBorderColor)));
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu private:
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ScrollViewWithBorder);
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)class ScrollCornerView : public views::View {
386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public:
396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  ScrollCornerView() {}
406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    ui::NativeTheme::ExtraParams ignored;
436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    GetNativeTheme()->Paint(canvas->sk_canvas(),
446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                            ui::NativeTheme::kScrollbarCorner,
456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                            ui::NativeTheme::kNormal,
466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                            GetLocalBounds(),
476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                            ignored);
486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private:
516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ScrollCornerView);
526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)};
536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns the position for the view so that it isn't scrolled off the visible
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// region.
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int CheckScrollBounds(int viewport_size, int content_size, int current_pos) {
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int max = std::max(content_size - viewport_size, 0);
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (current_pos < 0)
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 0;
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (current_pos > max)
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return max;
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return current_pos;
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Make sure the content is not scrolled out of bounds
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void CheckScrollBounds(View* viewport, View* view) {
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!view)
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int x = CheckScrollBounds(viewport->width(), view->width(), -view->x());
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int y = CheckScrollBounds(viewport->height(), view->height(), -view->y());
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This is no op if bounds are the same
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  view->SetBounds(-x, -y, view->width(), view->height());
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Used by ScrollToPosition() to make sure the new position fits within the
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// allowed scroll range.
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int AdjustPosition(int current_position,
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   int new_position,
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   int content_size,
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   int viewport_size) {
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (-current_position == new_position)
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return new_position;
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (new_position < 0)
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 0;
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int max_position = std::max(0, content_size - viewport_size);
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (new_position > max_position) ? max_position : new_position;
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Viewport contains the contents View of the ScrollView.
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class ScrollView::Viewport : public View {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Viewport() {}
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~Viewport() {}
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
99b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual const char* GetClassName() const OVERRIDE {
100b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return "ScrollView::Viewport";
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void ScrollRectToVisible(const gfx::Rect& rect) OVERRIDE {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!has_children() || !parent())
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    View* contents = child_at(0);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect scroll_rect(rect);
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scroll_rect.Offset(-contents->x(), -contents->y());
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    static_cast<ScrollView*>(parent())->ScrollContentsRegionToBeVisible(
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        scroll_rect);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void ChildPreferredSizeChanged(View* child) OVERRIDE {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (parent())
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      parent()->Layout();
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(Viewport);
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ScrollView::ScrollView()
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : contents_(NULL),
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      contents_viewport_(new Viewport()),
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      header_(NULL),
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      header_viewport_(new Viewport()),
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      horiz_sb_(new NativeScrollBar(true)),
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      vert_sb_(new NativeScrollBar(false)),
1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      corner_view_(new ScrollCornerView()),
131effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      min_height_(-1),
132effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      max_height_(-1),
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      hide_horizontal_scrollbar_(false) {
1345e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  set_notify_enter_exit_on_child(true);
1355e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(contents_viewport_);
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(header_viewport_);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Don't add the scrollbars as children until we discover we need them
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // (ShowOrHideScrollBar).
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  horiz_sb_->SetVisible(false);
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  horiz_sb_->set_controller(this);
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  vert_sb_->SetVisible(false);
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  vert_sb_->set_controller(this);
1456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  corner_view_->SetVisible(false);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ScrollView::~ScrollView() {
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The scrollbars may not have been added, delete them to ensure they get
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // deleted.
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete horiz_sb_;
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete vert_sb_;
1536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  delete corner_view_;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ScrollView* ScrollView::CreateScrollViewWithBorder() {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new ScrollViewWithBorder();
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ScrollView::SetContents(View* a_view) {
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetHeaderOrContents(contents_viewport_, a_view, &contents_);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::SetHeader(View* header) {
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetHeaderOrContents(header_viewport_, header, &header_);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Rect ScrollView::GetVisibleRect() const {
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!contents_)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return gfx::Rect();
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return gfx::Rect(-contents_->x(), -contents_->y(),
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   contents_viewport_->width(), contents_viewport_->height());
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
176effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid ScrollView::ClipHeightTo(int min_height, int max_height) {
177effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  min_height_ = min_height;
178effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  max_height_ = max_height;
179effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
180effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int ScrollView::GetScrollBarWidth() const {
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return vert_sb_ ? vert_sb_->GetLayoutSize() : 0;
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int ScrollView::GetScrollBarHeight() const {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return horiz_sb_ ? horiz_sb_->GetLayoutSize() : 0;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::SetHorizontalScrollBar(ScrollBar* horiz_sb) {
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(horiz_sb);
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  horiz_sb->SetVisible(horiz_sb_->visible());
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete horiz_sb_;
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  horiz_sb->set_controller(this);
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  horiz_sb_ = horiz_sb;
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::SetVerticalScrollBar(ScrollBar* vert_sb) {
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(vert_sb);
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  vert_sb->SetVisible(vert_sb_->visible());
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete vert_sb_;
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  vert_sb->set_controller(this);
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  vert_sb_ = vert_sb;
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)gfx::Size ScrollView::GetPreferredSize() const {
206effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!is_bounded())
207effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return View::GetPreferredSize();
208effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
209effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  gfx::Size size = contents()->GetPreferredSize();
210effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  size.SetToMax(gfx::Size(size.width(), min_height_));
211effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  size.SetToMin(gfx::Size(size.width(), max_height_));
212effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  gfx::Insets insets = GetInsets();
213effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  size.Enlarge(insets.width(), insets.height());
214effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return size;
215effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
216effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)int ScrollView::GetHeightForWidth(int width) const {
218effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!is_bounded())
219effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return View::GetHeightForWidth(width);
220effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
221effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  gfx::Insets insets = GetInsets();
222effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  width = std::max(0, width - insets.width());
223effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  int height = contents()->GetHeightForWidth(width) + insets.height();
224effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return std::min(std::max(height, min_height_), max_height_);
225effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
226effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ScrollView::Layout() {
228effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (is_bounded()) {
229effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    int content_width = width();
230effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    int content_height = contents()->GetHeightForWidth(content_width);
231effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (content_height > height()) {
232effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      content_width = std::max(content_width - GetScrollBarWidth(), 0);
233effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      content_height = contents()->GetHeightForWidth(content_width);
234effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
235effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (contents()->bounds().size() != gfx::Size(content_width, content_height))
236effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      contents()->SetBounds(0, 0, content_width, content_height);
237effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
238effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Most views will want to auto-fit the available space. Most of them want to
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // use all available width (without overflowing) and only overflow in
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // height. Examples are HistoryView, MostVisitedView, DownloadTabView, etc.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Other views want to fit in both ways. An example is PrintView. To make both
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // happy, assume a vertical scrollbar but no horizontal scrollbar. To override
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this default behavior, the inner view has to calculate the available space,
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // used ComputeScrollBarsVisibility() to use the same calculation that is done
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // here and sets its bound to fit within.
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect viewport_bounds = GetContentsBounds();
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int contents_x = viewport_bounds.x();
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int contents_y = viewport_bounds.y();
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (viewport_bounds.IsEmpty()) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // There's nothing to layout.
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int header_height =
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::min(viewport_bounds.height(),
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               header_ ? header_->GetPreferredSize().height() : 0);
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  viewport_bounds.set_height(
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::max(0, viewport_bounds.height() - header_height));
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  viewport_bounds.set_y(viewport_bounds.y() + header_height);
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // viewport_size is the total client space available.
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Size viewport_size = viewport_bounds.size();
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Assumes a vertical scrollbar since most of the current views are designed
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for this.
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int horiz_sb_height = GetScrollBarHeight();
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int vert_sb_width = GetScrollBarWidth();
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  viewport_bounds.set_width(viewport_bounds.width() - vert_sb_width);
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Update the bounds right now so the inner views can fit in it.
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  contents_viewport_->SetBoundsRect(viewport_bounds);
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Give |contents_| a chance to update its bounds if it depends on the
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // viewport.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (contents_)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    contents_->Layout();
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool should_layout_contents = false;
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool horiz_sb_required = false;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool vert_sb_required = false;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (contents_) {
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Size content_size = contents_->size();
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ComputeScrollBarsVisibility(viewport_size,
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                content_size,
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                &horiz_sb_required,
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                &vert_sb_required);
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  bool corner_view_required = horiz_sb_required && vert_sb_required;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Take action.
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetControlVisibility(horiz_sb_, horiz_sb_required);
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetControlVisibility(vert_sb_, vert_sb_required);
2906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  SetControlVisibility(corner_view_, corner_view_required);
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Non-default.
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (horiz_sb_required) {
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    viewport_bounds.set_height(
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::max(0, viewport_bounds.height() - horiz_sb_height));
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    should_layout_contents = true;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Default.
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!vert_sb_required) {
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    viewport_bounds.set_width(viewport_bounds.width() + vert_sb_width);
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    should_layout_contents = true;
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (horiz_sb_required) {
3055e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    int height_offset = horiz_sb_->GetContentOverlapSize();
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    horiz_sb_->SetBounds(0,
3075e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)                         viewport_bounds.bottom() - height_offset,
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         viewport_bounds.right(),
3095e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)                         horiz_sb_height + height_offset);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (vert_sb_required) {
3125e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    int width_offset = vert_sb_->GetContentOverlapSize();
3135e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    vert_sb_->SetBounds(viewport_bounds.right() - width_offset,
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        0,
3155e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)                        vert_sb_width + width_offset,
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        viewport_bounds.bottom());
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (corner_view_required) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Show the resize corner.
3206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    corner_view_->SetBounds(viewport_bounds.right(),
3216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                            viewport_bounds.bottom(),
3226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                            vert_sb_width,
3236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                            horiz_sb_height);
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Update to the real client size with the visible scrollbars.
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  contents_viewport_->SetBoundsRect(viewport_bounds);
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (should_layout_contents && contents_)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    contents_->Layout();
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  header_viewport_->SetBounds(contents_x, contents_y,
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              viewport_bounds.width(), header_height);
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (header_)
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    header_->Layout();
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CheckScrollBounds(header_viewport_, header_);
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CheckScrollBounds(contents_viewport_, contents_);
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateScrollBarPositions();
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ScrollView::OnKeyPressed(const ui::KeyEvent& event) {
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool processed = false;
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Give vertical scrollbar priority
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (vert_sb_->visible())
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    processed = vert_sb_->OnKeyPressed(event);
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!processed && horiz_sb_->visible())
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    processed = horiz_sb_->OnKeyPressed(event);
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return processed;
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ScrollView::OnMouseWheel(const ui::MouseWheelEvent& e) {
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool processed = false;
3571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (vert_sb_->visible())
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    processed = vert_sb_->OnMouseWheel(e);
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (horiz_sb_->visible())
3621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    processed = horiz_sb_->OnMouseWheel(e) || processed;
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return processed;
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3675e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)void ScrollView::OnMouseEntered(const ui::MouseEvent& event) {
3685e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  if (horiz_sb_)
3695e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    horiz_sb_->OnMouseEnteredScrollView(event);
3705e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  if (vert_sb_)
3715e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    vert_sb_->OnMouseEnteredScrollView(event);
3725e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)}
3735e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)
3745e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)void ScrollView::OnMouseExited(const ui::MouseEvent& event) {
3755e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  if (horiz_sb_)
3765e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    horiz_sb_->OnMouseExitedScrollView(event);
3775e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  if (vert_sb_)
3785e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    vert_sb_->OnMouseExitedScrollView(event);
3795e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)}
3805e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::OnGestureEvent(ui::GestureEvent* event) {
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If the event happened on one of the scrollbars, then those events are
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // sent directly to the scrollbars. Otherwise, only scroll events are sent to
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the scrollbars.
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool scroll_event = event->type() == ui::ET_GESTURE_SCROLL_UPDATE ||
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      event->type() == ui::ET_GESTURE_SCROLL_END ||
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      event->type() == ui::ET_SCROLL_FLING_START;
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (vert_sb_->visible()) {
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (vert_sb_->bounds().Contains(event->location()) || scroll_event)
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      vert_sb_->OnGestureEvent(event);
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!event->handled() && horiz_sb_->visible()) {
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (horiz_sb_->bounds().Contains(event->location()) || scroll_event)
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      horiz_sb_->OnGestureEvent(event);
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
400b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char* ScrollView::GetClassName() const {
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return kViewClassName;
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::ScrollToPosition(ScrollBar* source, int position) {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!contents_)
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (source == horiz_sb_ && horiz_sb_->visible()) {
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    position = AdjustPosition(contents_->x(), position, contents_->width(),
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              contents_viewport_->width());
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (-contents_->x() == position)
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    contents_->SetX(-position);
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (header_) {
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      header_->SetX(-position);
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      header_->SchedulePaintInRect(header_->GetVisibleBounds());
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (source == vert_sb_ && vert_sb_->visible()) {
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    position = AdjustPosition(contents_->y(), position, contents_->height(),
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              contents_viewport_->height());
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (-contents_->y() == position)
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    contents_->SetY(-position);
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  contents_->SchedulePaintInRect(contents_->GetVisibleBounds());
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int ScrollView::GetScrollIncrement(ScrollBar* source, bool is_page,
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   bool is_positive) {
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool is_horizontal = source->IsHorizontal();
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int amount = 0;
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (contents_) {
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (is_page) {
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      amount = contents_->GetPageScrollIncrement(
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          this, is_horizontal, is_positive);
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      amount = contents_->GetLineScrollIncrement(
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          this, is_horizontal, is_positive);
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (amount > 0)
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return amount;
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // No view, or the view didn't return a valid amount.
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (is_page) {
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return is_horizontal ? contents_viewport_->width() :
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           contents_viewport_->height();
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return is_horizontal ? contents_viewport_->width() / 5 :
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         contents_viewport_->height() / 5;
4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::SetHeaderOrContents(View* parent,
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     View* new_view,
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     View** member) {
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (*member == new_view)
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete *member;
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *member = new_view;
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (*member)
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    parent->AddChildView(*member);
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Layout();
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ScrollView::ScrollContentsRegionToBeVisible(const gfx::Rect& rect) {
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!contents_ || (!horiz_sb_->visible() && !vert_sb_->visible()))
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Figure out the maximums for this scroll view.
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int contents_max_x =
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::max(contents_viewport_->width(), contents_->width());
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int contents_max_y =
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::max(contents_viewport_->height(), contents_->height());
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure x and y are within the bounds of [0,contents_max_*].
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int x = std::max(0, std::min(contents_max_x, rect.x()));
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int y = std::max(0, std::min(contents_max_y, rect.y()));
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Figure out how far and down the rectangle will go taking width
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and height into account.  This will be "clipped" by the viewport.
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int max_x = std::min(contents_max_x,
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      x + std::min(rect.width(), contents_viewport_->width()));
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int max_y = std::min(contents_max_y,
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      y + std::min(rect.height(), contents_viewport_->height()));
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // See if the rect is already visible. Note the width is (max_x - x)
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and the height is (max_y - y) to take into account the clipping of
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // either viewport or the content size.
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const gfx::Rect vis_rect = GetVisibleRect();
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (vis_rect.Contains(gfx::Rect(x, y, max_x - x, max_y - y)))
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Shift contents_'s X and Y so that the region is visible. If we
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // need to shift up or left from where we currently are then we need
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // to get it so that the content appears in the upper/left
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // corner. This is done by setting the offset to -X or -Y.  For down
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // or right shifts we need to make sure it appears in the
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // lower/right corner. This is calculated by taking max_x or max_y
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and scaling it back by the size of the viewport.
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int new_x =
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (vis_rect.x() > x) ? x : std::max(0, max_x - contents_viewport_->width());
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int new_y =
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (vis_rect.y() > y) ? y : std::max(0, max_y -
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        contents_viewport_->height());
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  contents_->SetX(-new_x);
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (header_)
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    header_->SetX(-new_x);
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  contents_->SetY(-new_y);
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateScrollBarPositions();
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::ComputeScrollBarsVisibility(const gfx::Size& vp_size,
5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             const gfx::Size& content_size,
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             bool* horiz_is_shown,
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             bool* vert_is_shown) const {
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Try to fit both ways first, then try vertical bar only, then horizontal
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // bar only, then defaults to both shown.
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (content_size.width() <= vp_size.width() &&
5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content_size.height() <= vp_size.height()) {
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *horiz_is_shown = false;
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *vert_is_shown = false;
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (content_size.width() <= vp_size.width() - GetScrollBarWidth()) {
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *horiz_is_shown = false;
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *vert_is_shown = true;
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (content_size.height() <= vp_size.height() - GetScrollBarHeight()) {
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *horiz_is_shown = true;
5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *vert_is_shown = false;
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *horiz_is_shown = true;
5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *vert_is_shown = true;
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
533c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
534c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (hide_horizontal_scrollbar_)
535c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    *horiz_is_shown = false;
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Make sure that a single scrollbar is created and visible as needed
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::SetControlVisibility(View* control, bool should_show) {
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!control)
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (should_show) {
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!control->visible()) {
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AddChildView(control);
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      control->SetVisible(true);
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    RemoveChildView(control);
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    control->SetVisible(false);
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::UpdateScrollBarPositions() {
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!contents_)
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (horiz_sb_->visible()) {
5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int vw = contents_viewport_->width();
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int cw = contents_->width();
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int origin = contents_->x();
5612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    horiz_sb_->Update(vw, cw, -origin);
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (vert_sb_->visible()) {
5642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int vh = contents_viewport_->height();
5652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int ch = contents_->height();
5662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int origin = contents_->y();
5672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    vert_sb_->Update(vh, ch, -origin);
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// VariableRowHeightScrollHelper ----------------------------------------------
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)VariableRowHeightScrollHelper::VariableRowHeightScrollHelper(
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Controller* controller) : controller_(controller) {
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)VariableRowHeightScrollHelper::~VariableRowHeightScrollHelper() {
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int VariableRowHeightScrollHelper::GetPageScrollIncrement(
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ScrollView* scroll_view, bool is_horizontal, bool is_positive) {
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_horizontal)
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // y coordinate is most likely negative.
5852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int y = abs(scroll_view->contents()->y());
5862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int vis_height = scroll_view->contents()->parent()->height();
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_positive) {
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Align the bottom most row to the top of the view.
5892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int bottom = std::min(scroll_view->contents()->height() - 1,
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          y + vis_height);
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RowInfo bottom_row_info = GetRowInfo(bottom);
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If 0, ScrollView will provide a default value.
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return std::max(0, bottom_row_info.origin - y);
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Align the row on the previous page to to the top of the view.
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int last_page_y = y - vis_height;
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RowInfo last_page_info = GetRowInfo(std::max(0, last_page_y));
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (last_page_y != last_page_info.origin)
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return std::max(0, y - last_page_info.origin - last_page_info.height);
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return std::max(0, y - last_page_info.origin);
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int VariableRowHeightScrollHelper::GetLineScrollIncrement(
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ScrollView* scroll_view, bool is_horizontal, bool is_positive) {
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_horizontal)
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // y coordinate is most likely negative.
6092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int y = abs(scroll_view->contents()->y());
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RowInfo row = GetRowInfo(y);
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_positive) {
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return row.height - (y - row.origin);
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (y == row.origin) {
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    row = GetRowInfo(std::max(0, row.origin - 1));
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return y - row.origin;
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return y - row.origin;
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)VariableRowHeightScrollHelper::RowInfo
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VariableRowHeightScrollHelper::GetRowInfo(int y) {
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return controller_->GetRowInfo(y);
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// FixedRowHeightScrollHelper -----------------------------------------------
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FixedRowHeightScrollHelper::FixedRowHeightScrollHelper(int top_margin,
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       int row_height)
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : VariableRowHeightScrollHelper(NULL),
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      top_margin_(top_margin),
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      row_height_(row_height) {
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GT(row_height, 0);
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)VariableRowHeightScrollHelper::RowInfo
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FixedRowHeightScrollHelper::GetRowInfo(int y) {
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (y < top_margin_)
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return RowInfo(0, top_margin_);
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return RowInfo((y - top_margin_) / row_height_ * row_height_ + top_margin_,
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 row_height_);
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace views
645