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"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/native_theme/native_theme.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/border.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/scrollbar/native_scroll_bar.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/root_view.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace views {
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char ScrollView::kViewClassName[] = "ScrollView";
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Subclass of ScrollView that resets the border when the theme changes.
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ScrollViewWithBorder : public views::ScrollView {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScrollViewWithBorder() {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetThemeSpecificState();
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // View overrides;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetThemeSpecificState();
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetThemeSpecificState() {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_border(Border::CreateSolidBorder(
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        1, GetNativeTheme()->GetSystemColor(
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ui::NativeTheme::kColorId_UnfocusedBorderColor)));
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ScrollViewWithBorder);
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns the position for the view so that it isn't scrolled off the visible
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// region.
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int CheckScrollBounds(int viewport_size, int content_size, int current_pos) {
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int max = std::max(content_size - viewport_size, 0);
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (current_pos < 0)
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 0;
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (current_pos > max)
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return max;
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return current_pos;
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Make sure the content is not scrolled out of bounds
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void CheckScrollBounds(View* viewport, View* view) {
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!view)
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int x = CheckScrollBounds(viewport->width(), view->width(), -view->x());
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int y = CheckScrollBounds(viewport->height(), view->height(), -view->y());
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This is no op if bounds are the same
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  view->SetBounds(-x, -y, view->width(), view->height());
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Used by ScrollToPosition() to make sure the new position fits within the
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// allowed scroll range.
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int AdjustPosition(int current_position,
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   int new_position,
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   int content_size,
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   int viewport_size) {
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (-current_position == new_position)
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return new_position;
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (new_position < 0)
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 0;
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int max_position = std::max(0, content_size - viewport_size);
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (new_position > max_position) ? max_position : new_position;
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Viewport contains the contents View of the ScrollView.
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class ScrollView::Viewport : public View {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Viewport() {}
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~Viewport() {}
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
87b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual const char* GetClassName() const OVERRIDE {
88b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return "ScrollView::Viewport";
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void ScrollRectToVisible(const gfx::Rect& rect) OVERRIDE {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!has_children() || !parent())
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    View* contents = child_at(0);
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect scroll_rect(rect);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scroll_rect.Offset(-contents->x(), -contents->y());
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    static_cast<ScrollView*>(parent())->ScrollContentsRegionToBeVisible(
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        scroll_rect);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void ChildPreferredSizeChanged(View* child) OVERRIDE {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (parent())
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      parent()->Layout();
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(Viewport);
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ScrollView::ScrollView()
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : contents_(NULL),
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      contents_viewport_(new Viewport()),
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      header_(NULL),
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      header_viewport_(new Viewport()),
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      horiz_sb_(new NativeScrollBar(true)),
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      vert_sb_(new NativeScrollBar(false)),
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      resize_corner_(NULL),
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      hide_horizontal_scrollbar_(false) {
1205e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  set_notify_enter_exit_on_child(true);
1215e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(contents_viewport_);
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AddChildView(header_viewport_);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Don't add the scrollbars as children until we discover we need them
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // (ShowOrHideScrollBar).
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  horiz_sb_->SetVisible(false);
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  horiz_sb_->set_controller(this);
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  vert_sb_->SetVisible(false);
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  vert_sb_->set_controller(this);
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (resize_corner_)
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    resize_corner_->SetVisible(false);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ScrollView::~ScrollView() {
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The scrollbars may not have been added, delete them to ensure they get
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // deleted.
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete horiz_sb_;
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete vert_sb_;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (resize_corner_ && !resize_corner_->parent())
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete resize_corner_;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ScrollView* ScrollView::CreateScrollViewWithBorder() {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new ScrollViewWithBorder();
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ScrollView::SetContents(View* a_view) {
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetHeaderOrContents(contents_viewport_, a_view, &contents_);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::SetHeader(View* header) {
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetHeaderOrContents(header_viewport_, header, &header_);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Rect ScrollView::GetVisibleRect() const {
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!contents_)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return gfx::Rect();
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return gfx::Rect(-contents_->x(), -contents_->y(),
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   contents_viewport_->width(), contents_viewport_->height());
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int ScrollView::GetScrollBarWidth() const {
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return vert_sb_ ? vert_sb_->GetLayoutSize() : 0;
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int ScrollView::GetScrollBarHeight() const {
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return horiz_sb_ ? horiz_sb_->GetLayoutSize() : 0;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::SetHorizontalScrollBar(ScrollBar* horiz_sb) {
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(horiz_sb);
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  horiz_sb->SetVisible(horiz_sb_->visible());
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete horiz_sb_;
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  horiz_sb->set_controller(this);
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  horiz_sb_ = horiz_sb;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::SetVerticalScrollBar(ScrollBar* vert_sb) {
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(vert_sb);
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  vert_sb->SetVisible(vert_sb_->visible());
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete vert_sb_;
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  vert_sb->set_controller(this);
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  vert_sb_ = vert_sb;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ScrollView::Layout() {
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Most views will want to auto-fit the available space. Most of them want to
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // use all available width (without overflowing) and only overflow in
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // height. Examples are HistoryView, MostVisitedView, DownloadTabView, etc.
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Other views want to fit in both ways. An example is PrintView. To make both
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // happy, assume a vertical scrollbar but no horizontal scrollbar. To override
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this default behavior, the inner view has to calculate the available space,
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // used ComputeScrollBarsVisibility() to use the same calculation that is done
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // here and sets its bound to fit within.
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect viewport_bounds = GetContentsBounds();
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int contents_x = viewport_bounds.x();
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int contents_y = viewport_bounds.y();
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (viewport_bounds.IsEmpty()) {
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // There's nothing to layout.
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int header_height =
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::min(viewport_bounds.height(),
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               header_ ? header_->GetPreferredSize().height() : 0);
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  viewport_bounds.set_height(
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::max(0, viewport_bounds.height() - header_height));
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  viewport_bounds.set_y(viewport_bounds.y() + header_height);
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // viewport_size is the total client space available.
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Size viewport_size = viewport_bounds.size();
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Assumes a vertical scrollbar since most of the current views are designed
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for this.
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int horiz_sb_height = GetScrollBarHeight();
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int vert_sb_width = GetScrollBarWidth();
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  viewport_bounds.set_width(viewport_bounds.width() - vert_sb_width);
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Update the bounds right now so the inner views can fit in it.
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  contents_viewport_->SetBoundsRect(viewport_bounds);
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Give |contents_| a chance to update its bounds if it depends on the
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // viewport.
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (contents_)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    contents_->Layout();
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool should_layout_contents = false;
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool horiz_sb_required = false;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool vert_sb_required = false;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (contents_) {
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Size content_size = contents_->size();
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ComputeScrollBarsVisibility(viewport_size,
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                content_size,
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                &horiz_sb_required,
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                &vert_sb_required);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool resize_corner_required = resize_corner_ && horiz_sb_required &&
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                vert_sb_required;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Take action.
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetControlVisibility(horiz_sb_, horiz_sb_required);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetControlVisibility(vert_sb_, vert_sb_required);
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetControlVisibility(resize_corner_, resize_corner_required);
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Non-default.
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (horiz_sb_required) {
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    viewport_bounds.set_height(
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::max(0, viewport_bounds.height() - horiz_sb_height));
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    should_layout_contents = true;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Default.
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!vert_sb_required) {
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    viewport_bounds.set_width(viewport_bounds.width() + vert_sb_width);
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    should_layout_contents = true;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (horiz_sb_required) {
2575e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    int height_offset = horiz_sb_->GetContentOverlapSize();
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    horiz_sb_->SetBounds(0,
2595e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)                         viewport_bounds.bottom() - height_offset,
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         viewport_bounds.right(),
2615e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)                         horiz_sb_height + height_offset);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (vert_sb_required) {
2645e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    int width_offset = vert_sb_->GetContentOverlapSize();
2655e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    vert_sb_->SetBounds(viewport_bounds.right() - width_offset,
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        0,
2675e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)                        vert_sb_width + width_offset,
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        viewport_bounds.bottom());
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (resize_corner_required) {
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Show the resize corner.
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resize_corner_->SetBounds(viewport_bounds.right(),
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              viewport_bounds.bottom(),
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              vert_sb_width,
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              horiz_sb_height);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Update to the real client size with the visible scrollbars.
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  contents_viewport_->SetBoundsRect(viewport_bounds);
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (should_layout_contents && contents_)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    contents_->Layout();
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  header_viewport_->SetBounds(contents_x, contents_y,
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              viewport_bounds.width(), header_height);
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (header_)
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    header_->Layout();
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CheckScrollBounds(header_viewport_, header_);
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CheckScrollBounds(contents_viewport_, contents_);
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateScrollBarPositions();
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ScrollView::OnKeyPressed(const ui::KeyEvent& event) {
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool processed = false;
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Give vertical scrollbar priority
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (vert_sb_->visible())
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    processed = vert_sb_->OnKeyPressed(event);
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!processed && horiz_sb_->visible())
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    processed = horiz_sb_->OnKeyPressed(event);
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return processed;
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ScrollView::OnMouseWheel(const ui::MouseWheelEvent& e) {
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool processed = false;
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Give vertical scrollbar priority
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (vert_sb_->visible())
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    processed = vert_sb_->OnMouseWheel(e);
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!processed && horiz_sb_->visible())
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    processed = horiz_sb_->OnMouseWheel(e);
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return processed;
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3195e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)void ScrollView::OnMouseEntered(const ui::MouseEvent& event) {
3205e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  if (horiz_sb_)
3215e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    horiz_sb_->OnMouseEnteredScrollView(event);
3225e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  if (vert_sb_)
3235e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    vert_sb_->OnMouseEnteredScrollView(event);
3245e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)}
3255e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)
3265e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)void ScrollView::OnMouseExited(const ui::MouseEvent& event) {
3275e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  if (horiz_sb_)
3285e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    horiz_sb_->OnMouseExitedScrollView(event);
3295e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  if (vert_sb_)
3305e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    vert_sb_->OnMouseExitedScrollView(event);
3315e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)}
3325e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::OnGestureEvent(ui::GestureEvent* event) {
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If the event happened on one of the scrollbars, then those events are
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // sent directly to the scrollbars. Otherwise, only scroll events are sent to
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the scrollbars.
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool scroll_event = event->type() == ui::ET_GESTURE_SCROLL_UPDATE ||
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      event->type() == ui::ET_GESTURE_SCROLL_END ||
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      event->type() == ui::ET_SCROLL_FLING_START;
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (vert_sb_->visible()) {
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (vert_sb_->bounds().Contains(event->location()) || scroll_event)
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      vert_sb_->OnGestureEvent(event);
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!event->handled() && horiz_sb_->visible()) {
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (horiz_sb_->bounds().Contains(event->location()) || scroll_event)
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      horiz_sb_->OnGestureEvent(event);
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
352b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char* ScrollView::GetClassName() const {
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return kViewClassName;
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::ScrollToPosition(ScrollBar* source, int position) {
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!contents_)
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (source == horiz_sb_ && horiz_sb_->visible()) {
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    position = AdjustPosition(contents_->x(), position, contents_->width(),
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              contents_viewport_->width());
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (-contents_->x() == position)
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    contents_->SetX(-position);
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (header_) {
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      header_->SetX(-position);
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      header_->SchedulePaintInRect(header_->GetVisibleBounds());
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (source == vert_sb_ && vert_sb_->visible()) {
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    position = AdjustPosition(contents_->y(), position, contents_->height(),
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              contents_viewport_->height());
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (-contents_->y() == position)
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    contents_->SetY(-position);
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  contents_->SchedulePaintInRect(contents_->GetVisibleBounds());
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int ScrollView::GetScrollIncrement(ScrollBar* source, bool is_page,
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   bool is_positive) {
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool is_horizontal = source->IsHorizontal();
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int amount = 0;
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (contents_) {
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (is_page) {
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      amount = contents_->GetPageScrollIncrement(
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          this, is_horizontal, is_positive);
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      amount = contents_->GetLineScrollIncrement(
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          this, is_horizontal, is_positive);
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (amount > 0)
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return amount;
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // No view, or the view didn't return a valid amount.
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (is_page) {
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return is_horizontal ? contents_viewport_->width() :
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           contents_viewport_->height();
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return is_horizontal ? contents_viewport_->width() / 5 :
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         contents_viewport_->height() / 5;
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::SetHeaderOrContents(View* parent,
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     View* new_view,
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     View** member) {
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (*member == new_view)
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete *member;
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *member = new_view;
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (*member)
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    parent->AddChildView(*member);
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Layout();
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ScrollView::ScrollContentsRegionToBeVisible(const gfx::Rect& rect) {
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!contents_ || (!horiz_sb_->visible() && !vert_sb_->visible()))
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Figure out the maximums for this scroll view.
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int contents_max_x =
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::max(contents_viewport_->width(), contents_->width());
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int contents_max_y =
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::max(contents_viewport_->height(), contents_->height());
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure x and y are within the bounds of [0,contents_max_*].
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int x = std::max(0, std::min(contents_max_x, rect.x()));
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int y = std::max(0, std::min(contents_max_y, rect.y()));
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Figure out how far and down the rectangle will go taking width
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and height into account.  This will be "clipped" by the viewport.
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int max_x = std::min(contents_max_x,
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      x + std::min(rect.width(), contents_viewport_->width()));
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int max_y = std::min(contents_max_y,
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      y + std::min(rect.height(), contents_viewport_->height()));
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // See if the rect is already visible. Note the width is (max_x - x)
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and the height is (max_y - y) to take into account the clipping of
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // either viewport or the content size.
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const gfx::Rect vis_rect = GetVisibleRect();
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (vis_rect.Contains(gfx::Rect(x, y, max_x - x, max_y - y)))
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Shift contents_'s X and Y so that the region is visible. If we
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // need to shift up or left from where we currently are then we need
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // to get it so that the content appears in the upper/left
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // corner. This is done by setting the offset to -X or -Y.  For down
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // or right shifts we need to make sure it appears in the
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // lower/right corner. This is calculated by taking max_x or max_y
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and scaling it back by the size of the viewport.
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int new_x =
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (vis_rect.x() > x) ? x : std::max(0, max_x - contents_viewport_->width());
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int new_y =
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (vis_rect.y() > y) ? y : std::max(0, max_y -
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        contents_viewport_->height());
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  contents_->SetX(-new_x);
4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (header_)
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    header_->SetX(-new_x);
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  contents_->SetY(-new_y);
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateScrollBarPositions();
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::ComputeScrollBarsVisibility(const gfx::Size& vp_size,
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             const gfx::Size& content_size,
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             bool* horiz_is_shown,
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             bool* vert_is_shown) const {
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Try to fit both ways first, then try vertical bar only, then horizontal
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // bar only, then defaults to both shown.
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (content_size.width() <= vp_size.width() &&
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content_size.height() <= vp_size.height()) {
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *horiz_is_shown = false;
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *vert_is_shown = false;
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (content_size.width() <= vp_size.width() - GetScrollBarWidth()) {
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *horiz_is_shown = false;
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *vert_is_shown = true;
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (content_size.height() <= vp_size.height() - GetScrollBarHeight()) {
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *horiz_is_shown = true;
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *vert_is_shown = false;
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *horiz_is_shown = true;
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *vert_is_shown = true;
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
485c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
486c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (hide_horizontal_scrollbar_)
487c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    *horiz_is_shown = false;
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Make sure that a single scrollbar is created and visible as needed
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::SetControlVisibility(View* control, bool should_show) {
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!control)
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (should_show) {
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!control->visible()) {
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AddChildView(control);
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      control->SetVisible(true);
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    RemoveChildView(control);
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    control->SetVisible(false);
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScrollView::UpdateScrollBarPositions() {
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!contents_)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (horiz_sb_->visible()) {
5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int vw = contents_viewport_->width();
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int cw = contents_->width();
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int origin = contents_->x();
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    horiz_sb_->Update(vw, cw, -origin);
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (vert_sb_->visible()) {
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int vh = contents_viewport_->height();
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int ch = contents_->height();
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int origin = contents_->y();
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    vert_sb_->Update(vh, ch, -origin);
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// VariableRowHeightScrollHelper ----------------------------------------------
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)VariableRowHeightScrollHelper::VariableRowHeightScrollHelper(
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Controller* controller) : controller_(controller) {
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)VariableRowHeightScrollHelper::~VariableRowHeightScrollHelper() {
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int VariableRowHeightScrollHelper::GetPageScrollIncrement(
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ScrollView* scroll_view, bool is_horizontal, bool is_positive) {
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_horizontal)
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // y coordinate is most likely negative.
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int y = abs(scroll_view->contents()->y());
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int vis_height = scroll_view->contents()->parent()->height();
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_positive) {
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Align the bottom most row to the top of the view.
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int bottom = std::min(scroll_view->contents()->height() - 1,
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          y + vis_height);
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RowInfo bottom_row_info = GetRowInfo(bottom);
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If 0, ScrollView will provide a default value.
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return std::max(0, bottom_row_info.origin - y);
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Align the row on the previous page to to the top of the view.
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int last_page_y = y - vis_height;
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RowInfo last_page_info = GetRowInfo(std::max(0, last_page_y));
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (last_page_y != last_page_info.origin)
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return std::max(0, y - last_page_info.origin - last_page_info.height);
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return std::max(0, y - last_page_info.origin);
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int VariableRowHeightScrollHelper::GetLineScrollIncrement(
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ScrollView* scroll_view, bool is_horizontal, bool is_positive) {
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_horizontal)
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // y coordinate is most likely negative.
5612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int y = abs(scroll_view->contents()->y());
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RowInfo row = GetRowInfo(y);
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_positive) {
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return row.height - (y - row.origin);
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (y == row.origin) {
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    row = GetRowInfo(std::max(0, row.origin - 1));
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return y - row.origin;
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return y - row.origin;
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)VariableRowHeightScrollHelper::RowInfo
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VariableRowHeightScrollHelper::GetRowInfo(int y) {
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return controller_->GetRowInfo(y);
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// FixedRowHeightScrollHelper -----------------------------------------------
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FixedRowHeightScrollHelper::FixedRowHeightScrollHelper(int top_margin,
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       int row_height)
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : VariableRowHeightScrollHelper(NULL),
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      top_margin_(top_margin),
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      row_height_(row_height) {
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GT(row_height, 0);
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)VariableRowHeightScrollHelper::RowInfo
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FixedRowHeightScrollHelper::GetRowInfo(int y) {
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (y < top_margin_)
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return RowInfo(0, top_margin_);
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return RowInfo((y - top_margin_) / row_height_ * row_height_ + top_margin_,
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 row_height_);
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace views
597