Home | History | Annotate | Download | only in controls
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/views/controls/scroll_view.h"
      6 
      7 #include "testing/gtest/include/gtest/gtest.h"
      8 #include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
      9 #include "ui/views/test/test_views.h"
     10 
     11 namespace views {
     12 
     13 namespace {
     14 
     15 const int kWidth = 100;
     16 const int kMinHeight = 50;
     17 const int kMaxHeight = 100;
     18 
     19 // View implementation that allows setting the preferred size.
     20 class CustomView : public View {
     21  public:
     22   CustomView() {}
     23 
     24   void SetPreferredSize(const gfx::Size& size) {
     25     preferred_size_ = size;
     26     PreferredSizeChanged();
     27   }
     28 
     29   virtual gfx::Size GetPreferredSize() const OVERRIDE {
     30     return preferred_size_;
     31   }
     32 
     33   virtual void Layout() OVERRIDE {
     34     gfx::Size pref = GetPreferredSize();
     35     int width = pref.width();
     36     int height = pref.height();
     37     if (parent()) {
     38       width = std::max(parent()->width(), width);
     39       height = std::max(parent()->height(), height);
     40     }
     41     SetBounds(x(), y(), width, height);
     42   }
     43 
     44  private:
     45   gfx::Size preferred_size_;
     46 
     47   DISALLOW_COPY_AND_ASSIGN(CustomView);
     48 };
     49 
     50 }  // namespace
     51 
     52 // Verifies the viewport is sized to fit the available space.
     53 TEST(ScrollViewTest, ViewportSizedToFit) {
     54   ScrollView scroll_view;
     55   View* contents = new View;
     56   scroll_view.SetContents(contents);
     57   scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
     58   scroll_view.Layout();
     59   EXPECT_EQ("0,0 100x100", contents->parent()->bounds().ToString());
     60 }
     61 
     62 // Verifies the scrollbars are added as necessary.
     63 TEST(ScrollViewTest, ScrollBars) {
     64   ScrollView scroll_view;
     65   View* contents = new View;
     66   scroll_view.SetContents(contents);
     67   scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
     68 
     69   // Size the contents such that vertical scrollbar is needed.
     70   contents->SetBounds(0, 0, 50, 400);
     71   scroll_view.Layout();
     72   EXPECT_EQ(100 - scroll_view.GetScrollBarWidth(), contents->parent()->width());
     73   EXPECT_EQ(100, contents->parent()->height());
     74   EXPECT_TRUE(!scroll_view.horizontal_scroll_bar() ||
     75               !scroll_view.horizontal_scroll_bar()->visible());
     76   ASSERT_TRUE(scroll_view.vertical_scroll_bar() != NULL);
     77   EXPECT_TRUE(scroll_view.vertical_scroll_bar()->visible());
     78 
     79   // Size the contents such that horizontal scrollbar is needed.
     80   contents->SetBounds(0, 0, 400, 50);
     81   scroll_view.Layout();
     82   EXPECT_EQ(100, contents->parent()->width());
     83   EXPECT_EQ(100 - scroll_view.GetScrollBarHeight(),
     84             contents->parent()->height());
     85   ASSERT_TRUE(scroll_view.horizontal_scroll_bar() != NULL);
     86   EXPECT_TRUE(scroll_view.horizontal_scroll_bar()->visible());
     87   EXPECT_TRUE(!scroll_view.vertical_scroll_bar() ||
     88               !scroll_view.vertical_scroll_bar()->visible());
     89 
     90   // Both horizontal and vertical.
     91   contents->SetBounds(0, 0, 300, 400);
     92   scroll_view.Layout();
     93   EXPECT_EQ(100 - scroll_view.GetScrollBarWidth(), contents->parent()->width());
     94   EXPECT_EQ(100 - scroll_view.GetScrollBarHeight(),
     95             contents->parent()->height());
     96   ASSERT_TRUE(scroll_view.horizontal_scroll_bar() != NULL);
     97   EXPECT_TRUE(scroll_view.horizontal_scroll_bar()->visible());
     98   ASSERT_TRUE(scroll_view.vertical_scroll_bar() != NULL);
     99   EXPECT_TRUE(scroll_view.vertical_scroll_bar()->visible());
    100 }
    101 
    102 // Assertions around adding a header.
    103 TEST(ScrollViewTest, Header) {
    104   ScrollView scroll_view;
    105   View* contents = new View;
    106   CustomView* header = new CustomView;
    107   scroll_view.SetHeader(header);
    108   View* header_parent = header->parent();
    109   scroll_view.SetContents(contents);
    110   scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
    111   scroll_view.Layout();
    112   // |header|s preferred size is empty, which should result in all space going
    113   // to contents.
    114   EXPECT_EQ("0,0 100x0", header->parent()->bounds().ToString());
    115   EXPECT_EQ("0,0 100x100", contents->parent()->bounds().ToString());
    116 
    117   // Get the header a height of 20.
    118   header->SetPreferredSize(gfx::Size(10, 20));
    119   EXPECT_EQ("0,0 100x20", header->parent()->bounds().ToString());
    120   EXPECT_EQ("0,20 100x80", contents->parent()->bounds().ToString());
    121 
    122   // Remove the header.
    123   scroll_view.SetHeader(NULL);
    124   // SetHeader(NULL) deletes header.
    125   header = NULL;
    126   EXPECT_EQ("0,0 100x0", header_parent->bounds().ToString());
    127   EXPECT_EQ("0,0 100x100", contents->parent()->bounds().ToString());
    128 }
    129 
    130 // Verifies the scrollbars are added as necessary when a header is present.
    131 TEST(ScrollViewTest, ScrollBarsWithHeader) {
    132   ScrollView scroll_view;
    133   View* contents = new View;
    134   scroll_view.SetContents(contents);
    135   CustomView* header = new CustomView;
    136   scroll_view.SetHeader(header);
    137   scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
    138 
    139   header->SetPreferredSize(gfx::Size(10, 20));
    140 
    141   // Size the contents such that vertical scrollbar is needed.
    142   contents->SetBounds(0, 0, 50, 400);
    143   scroll_view.Layout();
    144   EXPECT_EQ(0, contents->parent()->x());
    145   EXPECT_EQ(20, contents->parent()->y());
    146   EXPECT_EQ(100 - scroll_view.GetScrollBarWidth(), contents->parent()->width());
    147   EXPECT_EQ(80, contents->parent()->height());
    148   EXPECT_EQ(0, header->parent()->x());
    149   EXPECT_EQ(0, header->parent()->y());
    150   EXPECT_EQ(100 - scroll_view.GetScrollBarWidth(), header->parent()->width());
    151   EXPECT_EQ(20, header->parent()->height());
    152   EXPECT_TRUE(!scroll_view.horizontal_scroll_bar() ||
    153               !scroll_view.horizontal_scroll_bar()->visible());
    154   ASSERT_TRUE(scroll_view.vertical_scroll_bar() != NULL);
    155   EXPECT_TRUE(scroll_view.vertical_scroll_bar()->visible());
    156 
    157   // Size the contents such that horizontal scrollbar is needed.
    158   contents->SetBounds(0, 0, 400, 50);
    159   scroll_view.Layout();
    160   EXPECT_EQ(0, contents->parent()->x());
    161   EXPECT_EQ(20, contents->parent()->y());
    162   EXPECT_EQ(100, contents->parent()->width());
    163   EXPECT_EQ(100 - scroll_view.GetScrollBarHeight() - 20,
    164             contents->parent()->height());
    165   EXPECT_EQ(0, header->parent()->x());
    166   EXPECT_EQ(0, header->parent()->y());
    167   EXPECT_EQ(100, header->parent()->width());
    168   EXPECT_EQ(20, header->parent()->height());
    169   ASSERT_TRUE(scroll_view.horizontal_scroll_bar() != NULL);
    170   EXPECT_TRUE(scroll_view.horizontal_scroll_bar()->visible());
    171   EXPECT_TRUE(!scroll_view.vertical_scroll_bar() ||
    172               !scroll_view.vertical_scroll_bar()->visible());
    173 
    174   // Both horizontal and vertical.
    175   contents->SetBounds(0, 0, 300, 400);
    176   scroll_view.Layout();
    177   EXPECT_EQ(0, contents->parent()->x());
    178   EXPECT_EQ(20, contents->parent()->y());
    179   EXPECT_EQ(100 - scroll_view.GetScrollBarWidth(), contents->parent()->width());
    180   EXPECT_EQ(100 - scroll_view.GetScrollBarHeight() - 20,
    181             contents->parent()->height());
    182   EXPECT_EQ(0, header->parent()->x());
    183   EXPECT_EQ(0, header->parent()->y());
    184   EXPECT_EQ(100 - scroll_view.GetScrollBarWidth(), header->parent()->width());
    185   EXPECT_EQ(20, header->parent()->height());
    186   ASSERT_TRUE(scroll_view.horizontal_scroll_bar() != NULL);
    187   EXPECT_TRUE(scroll_view.horizontal_scroll_bar()->visible());
    188   ASSERT_TRUE(scroll_view.vertical_scroll_bar() != NULL);
    189   EXPECT_TRUE(scroll_view.vertical_scroll_bar()->visible());
    190 }
    191 
    192 // Verifies the header scrolls horizontally with the content.
    193 TEST(ScrollViewTest, HeaderScrollsWithContent) {
    194   ScrollView scroll_view;
    195   CustomView* contents = new CustomView;
    196   scroll_view.SetContents(contents);
    197   contents->SetPreferredSize(gfx::Size(500, 500));
    198 
    199   CustomView* header = new CustomView;
    200   scroll_view.SetHeader(header);
    201   header->SetPreferredSize(gfx::Size(500, 20));
    202 
    203   scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
    204   EXPECT_EQ("0,0", contents->bounds().origin().ToString());
    205   EXPECT_EQ("0,0", header->bounds().origin().ToString());
    206 
    207   // Scroll the horizontal scrollbar.
    208   ASSERT_TRUE(scroll_view.horizontal_scroll_bar());
    209   scroll_view.ScrollToPosition(
    210       const_cast<ScrollBar*>(scroll_view.horizontal_scroll_bar()), 1);
    211   EXPECT_EQ("-1,0", contents->bounds().origin().ToString());
    212   EXPECT_EQ("-1,0", header->bounds().origin().ToString());
    213 
    214   // Scrolling the vertical scrollbar shouldn't effect the header.
    215   ASSERT_TRUE(scroll_view.vertical_scroll_bar());
    216   scroll_view.ScrollToPosition(
    217       const_cast<ScrollBar*>(scroll_view.vertical_scroll_bar()), 1);
    218   EXPECT_EQ("-1,-1", contents->bounds().origin().ToString());
    219   EXPECT_EQ("-1,0", header->bounds().origin().ToString());
    220 }
    221 
    222 // Verifies ScrollRectToVisible() on the child works.
    223 TEST(ScrollViewTest, ScrollRectToVisible) {
    224   ScrollView scroll_view;
    225   CustomView* contents = new CustomView;
    226   scroll_view.SetContents(contents);
    227   contents->SetPreferredSize(gfx::Size(500, 1000));
    228 
    229   scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
    230   scroll_view.Layout();
    231   EXPECT_EQ("0,0", contents->bounds().origin().ToString());
    232 
    233   // Scroll to y=405 height=10, this should make the y position of the content
    234   // at (405 + 10) - viewport_height (scroll region bottom aligned).
    235   contents->ScrollRectToVisible(gfx::Rect(0, 405, 10, 10));
    236   const int viewport_height = contents->parent()->height();
    237   EXPECT_EQ(-(415 - viewport_height), contents->y());
    238 
    239   // Scroll to the current y-location and 10x10; should do nothing.
    240   contents->ScrollRectToVisible(gfx::Rect(0, -contents->y(), 10, 10));
    241   EXPECT_EQ(-(415 - viewport_height), contents->y());
    242 }
    243 
    244 // Verifies ClipHeightTo() uses the height of the content when it is between the
    245 // minimum and maximum height values.
    246 TEST(ScrollViewTest, ClipHeightToNormalContentHeight) {
    247   ScrollView scroll_view;
    248 
    249   scroll_view.ClipHeightTo(kMinHeight, kMaxHeight);
    250 
    251   const int kNormalContentHeight = 75;
    252   scroll_view.SetContents(
    253       new views::StaticSizedView(gfx::Size(kWidth, kNormalContentHeight)));
    254 
    255   EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight),
    256             scroll_view.GetPreferredSize());
    257 
    258   scroll_view.SizeToPreferredSize();
    259   scroll_view.Layout();
    260 
    261   EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight),
    262             scroll_view.contents()->size());
    263   EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight), scroll_view.size());
    264 }
    265 
    266 // Verifies ClipHeightTo() uses the minimum height when the content is shorter
    267 // thamn the minimum height value.
    268 TEST(ScrollViewTest, ClipHeightToShortContentHeight) {
    269   ScrollView scroll_view;
    270 
    271   scroll_view.ClipHeightTo(kMinHeight, kMaxHeight);
    272 
    273   const int kShortContentHeight = 10;
    274   scroll_view.SetContents(
    275       new views::StaticSizedView(gfx::Size(kWidth, kShortContentHeight)));
    276 
    277   EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view.GetPreferredSize());
    278 
    279   scroll_view.SizeToPreferredSize();
    280   scroll_view.Layout();
    281 
    282   EXPECT_EQ(gfx::Size(kWidth, kShortContentHeight),
    283             scroll_view.contents()->size());
    284   EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view.size());
    285 }
    286 
    287 // Verifies ClipHeightTo() uses the maximum height when the content is longer
    288 // thamn the maximum height value.
    289 TEST(ScrollViewTest, ClipHeightToTallContentHeight) {
    290   ScrollView scroll_view;
    291 
    292   // Use a scrollbar that is disabled by default, so the width of the content is
    293   // not affected.
    294   scroll_view.SetVerticalScrollBar(new views::OverlayScrollBar(false));
    295 
    296   scroll_view.ClipHeightTo(kMinHeight, kMaxHeight);
    297 
    298   const int kTallContentHeight = 1000;
    299   scroll_view.SetContents(
    300       new views::StaticSizedView(gfx::Size(kWidth, kTallContentHeight)));
    301 
    302   EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view.GetPreferredSize());
    303 
    304   scroll_view.SizeToPreferredSize();
    305   scroll_view.Layout();
    306 
    307   EXPECT_EQ(gfx::Size(kWidth, kTallContentHeight),
    308             scroll_view.contents()->size());
    309   EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view.size());
    310 }
    311 
    312 // Verifies that when ClipHeightTo() produces a scrollbar, it reduces the width
    313 // of the inner content of the ScrollView.
    314 TEST(ScrollViewTest, ClipHeightToScrollbarUsesWidth) {
    315   ScrollView scroll_view;
    316 
    317   scroll_view.ClipHeightTo(kMinHeight, kMaxHeight);
    318 
    319   // Create a view that will be much taller than it is wide.
    320   scroll_view.SetContents(new views::ProportionallySizedView(1000));
    321 
    322   // Without any width, it will default to 0,0 but be overridden by min height.
    323   scroll_view.SizeToPreferredSize();
    324   EXPECT_EQ(gfx::Size(0, kMinHeight), scroll_view.GetPreferredSize());
    325 
    326   gfx::Size new_size(kWidth, scroll_view.GetHeightForWidth(kWidth));
    327   scroll_view.SetSize(new_size);
    328   scroll_view.Layout();
    329 
    330   int scroll_bar_width = scroll_view.GetScrollBarWidth();
    331   int expected_width = kWidth - scroll_bar_width;
    332   EXPECT_EQ(scroll_view.contents()->size().width(), expected_width);
    333   EXPECT_EQ(scroll_view.contents()->size().height(), 1000 * expected_width);
    334   EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view.size());
    335 }
    336 
    337 TEST(ScrollViewTest, CornerViewVisibility) {
    338   ScrollView scroll_view;
    339   View* contents = new View;
    340   scroll_view.SetContents(contents);
    341   scroll_view.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
    342   View* corner_view = scroll_view.corner_view_;
    343 
    344   // Corner view should be visible when both scrollbars are visible.
    345   contents->SetBounds(0, 0, 200, 200);
    346   scroll_view.Layout();
    347   EXPECT_EQ(&scroll_view, corner_view->parent());
    348   EXPECT_TRUE(corner_view->visible());
    349 
    350   // Corner view should be aligned to the scrollbars.
    351   EXPECT_EQ(scroll_view.vertical_scroll_bar()->x(), corner_view->x());
    352   EXPECT_EQ(scroll_view.horizontal_scroll_bar()->y(), corner_view->y());
    353   EXPECT_EQ(scroll_view.GetScrollBarWidth(), corner_view->width());
    354   EXPECT_EQ(scroll_view.GetScrollBarHeight(), corner_view->height());
    355 
    356   // Corner view should be removed when only the vertical scrollbar is visible.
    357   contents->SetBounds(0, 0, 50, 200);
    358   scroll_view.Layout();
    359   EXPECT_FALSE(corner_view->parent());
    360 
    361   // ... or when only the horizontal scrollbar is visible.
    362   contents->SetBounds(0, 0, 200, 50);
    363   scroll_view.Layout();
    364   EXPECT_FALSE(corner_view->parent());
    365 
    366   // ... or when no scrollbar is visible.
    367   contents->SetBounds(0, 0, 50, 50);
    368   scroll_view.Layout();
    369   EXPECT_FALSE(corner_view->parent());
    370 
    371   // Corner view should reappear when both scrollbars reappear.
    372   contents->SetBounds(0, 0, 200, 200);
    373   scroll_view.Layout();
    374   EXPECT_EQ(&scroll_view, corner_view->parent());
    375   EXPECT_TRUE(corner_view->visible());
    376 }
    377 
    378 }  // namespace views
    379