• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
CustomView()22   CustomView() {}
23 
SetPreferredSize(const gfx::Size & size)24   void SetPreferredSize(const gfx::Size& size) {
25     preferred_size_ = size;
26     PreferredSizeChanged();
27   }
28 
GetPreferredSize() const29   virtual gfx::Size GetPreferredSize() const OVERRIDE {
30     return preferred_size_;
31   }
32 
Layout()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.
TEST(ScrollViewTest,ViewportSizedToFit)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.
TEST(ScrollViewTest,ScrollBars)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.
TEST(ScrollViewTest,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.
TEST(ScrollViewTest,ScrollBarsWithHeader)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.
TEST(ScrollViewTest,HeaderScrollsWithContent)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.
TEST(ScrollViewTest,ScrollRectToVisible)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.
TEST(ScrollViewTest,ClipHeightToNormalContentHeight)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.
TEST(ScrollViewTest,ClipHeightToShortContentHeight)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.
TEST(ScrollViewTest,ClipHeightToTallContentHeight)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.
TEST(ScrollViewTest,ClipHeightToScrollbarUsesWidth)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 
TEST(ScrollViewTest,CornerViewVisibility)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