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.
5 #include "ui/views/controls/scroll_view.h"
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"
15 const int kWidth
= 100;
16 const int kMinHeight
= 50;
17 const int kMaxHeight
= 100;
19 // View implementation that allows setting the preferred size.
20 class CustomView
: public View
{
24 void SetPreferredSize(const gfx::Size
& size
) {
25 preferred_size_
= size
;
26 PreferredSizeChanged();
29 gfx::Size
GetPreferredSize() const override
{ return preferred_size_
; }
31 void Layout() override
{
32 gfx::Size pref
= GetPreferredSize();
33 int width
= pref
.width();
34 int height
= pref
.height();
36 width
= std::max(parent()->width(), width
);
37 height
= std::max(parent()->height(), height
);
39 SetBounds(x(), y(), width
, height
);
43 gfx::Size preferred_size_
;
45 DISALLOW_COPY_AND_ASSIGN(CustomView
);
50 // Verifies the viewport is sized to fit the available space.
51 TEST(ScrollViewTest
, ViewportSizedToFit
) {
52 ScrollView scroll_view
;
53 View
* contents
= new View
;
54 scroll_view
.SetContents(contents
);
55 scroll_view
.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
57 EXPECT_EQ("0,0 100x100", contents
->parent()->bounds().ToString());
60 // Verifies the scrollbars are added as necessary.
61 TEST(ScrollViewTest
, ScrollBars
) {
62 ScrollView scroll_view
;
63 View
* contents
= new View
;
64 scroll_view
.SetContents(contents
);
65 scroll_view
.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
67 // Size the contents such that vertical scrollbar is needed.
68 contents
->SetBounds(0, 0, 50, 400);
70 EXPECT_EQ(100 - scroll_view
.GetScrollBarWidth(), contents
->parent()->width());
71 EXPECT_EQ(100, contents
->parent()->height());
72 EXPECT_TRUE(!scroll_view
.horizontal_scroll_bar() ||
73 !scroll_view
.horizontal_scroll_bar()->visible());
74 ASSERT_TRUE(scroll_view
.vertical_scroll_bar() != NULL
);
75 EXPECT_TRUE(scroll_view
.vertical_scroll_bar()->visible());
77 // Size the contents such that horizontal scrollbar is needed.
78 contents
->SetBounds(0, 0, 400, 50);
80 EXPECT_EQ(100, contents
->parent()->width());
81 EXPECT_EQ(100 - scroll_view
.GetScrollBarHeight(),
82 contents
->parent()->height());
83 ASSERT_TRUE(scroll_view
.horizontal_scroll_bar() != NULL
);
84 EXPECT_TRUE(scroll_view
.horizontal_scroll_bar()->visible());
85 EXPECT_TRUE(!scroll_view
.vertical_scroll_bar() ||
86 !scroll_view
.vertical_scroll_bar()->visible());
88 // Both horizontal and vertical.
89 contents
->SetBounds(0, 0, 300, 400);
91 EXPECT_EQ(100 - scroll_view
.GetScrollBarWidth(), contents
->parent()->width());
92 EXPECT_EQ(100 - scroll_view
.GetScrollBarHeight(),
93 contents
->parent()->height());
94 ASSERT_TRUE(scroll_view
.horizontal_scroll_bar() != NULL
);
95 EXPECT_TRUE(scroll_view
.horizontal_scroll_bar()->visible());
96 ASSERT_TRUE(scroll_view
.vertical_scroll_bar() != NULL
);
97 EXPECT_TRUE(scroll_view
.vertical_scroll_bar()->visible());
100 // Assertions around adding a header.
101 TEST(ScrollViewTest
, Header
) {
102 ScrollView scroll_view
;
103 View
* contents
= new View
;
104 CustomView
* header
= new CustomView
;
105 scroll_view
.SetHeader(header
);
106 View
* header_parent
= header
->parent();
107 scroll_view
.SetContents(contents
);
108 scroll_view
.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
109 scroll_view
.Layout();
110 // |header|s preferred size is empty, which should result in all space going
112 EXPECT_EQ("0,0 100x0", header
->parent()->bounds().ToString());
113 EXPECT_EQ("0,0 100x100", contents
->parent()->bounds().ToString());
115 // Get the header a height of 20.
116 header
->SetPreferredSize(gfx::Size(10, 20));
117 EXPECT_EQ("0,0 100x20", header
->parent()->bounds().ToString());
118 EXPECT_EQ("0,20 100x80", contents
->parent()->bounds().ToString());
120 // Remove the header.
121 scroll_view
.SetHeader(NULL
);
122 // SetHeader(NULL) deletes header.
124 EXPECT_EQ("0,0 100x0", header_parent
->bounds().ToString());
125 EXPECT_EQ("0,0 100x100", contents
->parent()->bounds().ToString());
128 // Verifies the scrollbars are added as necessary when a header is present.
129 TEST(ScrollViewTest
, ScrollBarsWithHeader
) {
130 ScrollView scroll_view
;
131 View
* contents
= new View
;
132 scroll_view
.SetContents(contents
);
133 CustomView
* header
= new CustomView
;
134 scroll_view
.SetHeader(header
);
135 scroll_view
.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
137 header
->SetPreferredSize(gfx::Size(10, 20));
139 // Size the contents such that vertical scrollbar is needed.
140 contents
->SetBounds(0, 0, 50, 400);
141 scroll_view
.Layout();
142 EXPECT_EQ(0, contents
->parent()->x());
143 EXPECT_EQ(20, contents
->parent()->y());
144 EXPECT_EQ(100 - scroll_view
.GetScrollBarWidth(), contents
->parent()->width());
145 EXPECT_EQ(80, contents
->parent()->height());
146 EXPECT_EQ(0, header
->parent()->x());
147 EXPECT_EQ(0, header
->parent()->y());
148 EXPECT_EQ(100 - scroll_view
.GetScrollBarWidth(), header
->parent()->width());
149 EXPECT_EQ(20, header
->parent()->height());
150 EXPECT_TRUE(!scroll_view
.horizontal_scroll_bar() ||
151 !scroll_view
.horizontal_scroll_bar()->visible());
152 ASSERT_TRUE(scroll_view
.vertical_scroll_bar() != NULL
);
153 EXPECT_TRUE(scroll_view
.vertical_scroll_bar()->visible());
155 // Size the contents such that horizontal scrollbar is needed.
156 contents
->SetBounds(0, 0, 400, 50);
157 scroll_view
.Layout();
158 EXPECT_EQ(0, contents
->parent()->x());
159 EXPECT_EQ(20, contents
->parent()->y());
160 EXPECT_EQ(100, contents
->parent()->width());
161 EXPECT_EQ(100 - scroll_view
.GetScrollBarHeight() - 20,
162 contents
->parent()->height());
163 EXPECT_EQ(0, header
->parent()->x());
164 EXPECT_EQ(0, header
->parent()->y());
165 EXPECT_EQ(100, header
->parent()->width());
166 EXPECT_EQ(20, header
->parent()->height());
167 ASSERT_TRUE(scroll_view
.horizontal_scroll_bar() != NULL
);
168 EXPECT_TRUE(scroll_view
.horizontal_scroll_bar()->visible());
169 EXPECT_TRUE(!scroll_view
.vertical_scroll_bar() ||
170 !scroll_view
.vertical_scroll_bar()->visible());
172 // Both horizontal and vertical.
173 contents
->SetBounds(0, 0, 300, 400);
174 scroll_view
.Layout();
175 EXPECT_EQ(0, contents
->parent()->x());
176 EXPECT_EQ(20, contents
->parent()->y());
177 EXPECT_EQ(100 - scroll_view
.GetScrollBarWidth(), contents
->parent()->width());
178 EXPECT_EQ(100 - scroll_view
.GetScrollBarHeight() - 20,
179 contents
->parent()->height());
180 EXPECT_EQ(0, header
->parent()->x());
181 EXPECT_EQ(0, header
->parent()->y());
182 EXPECT_EQ(100 - scroll_view
.GetScrollBarWidth(), header
->parent()->width());
183 EXPECT_EQ(20, header
->parent()->height());
184 ASSERT_TRUE(scroll_view
.horizontal_scroll_bar() != NULL
);
185 EXPECT_TRUE(scroll_view
.horizontal_scroll_bar()->visible());
186 ASSERT_TRUE(scroll_view
.vertical_scroll_bar() != NULL
);
187 EXPECT_TRUE(scroll_view
.vertical_scroll_bar()->visible());
190 // Verifies the header scrolls horizontally with the content.
191 TEST(ScrollViewTest
, HeaderScrollsWithContent
) {
192 ScrollView scroll_view
;
193 CustomView
* contents
= new CustomView
;
194 scroll_view
.SetContents(contents
);
195 contents
->SetPreferredSize(gfx::Size(500, 500));
197 CustomView
* header
= new CustomView
;
198 scroll_view
.SetHeader(header
);
199 header
->SetPreferredSize(gfx::Size(500, 20));
201 scroll_view
.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
202 EXPECT_EQ("0,0", contents
->bounds().origin().ToString());
203 EXPECT_EQ("0,0", header
->bounds().origin().ToString());
205 // Scroll the horizontal scrollbar.
206 ASSERT_TRUE(scroll_view
.horizontal_scroll_bar());
207 scroll_view
.ScrollToPosition(
208 const_cast<ScrollBar
*>(scroll_view
.horizontal_scroll_bar()), 1);
209 EXPECT_EQ("-1,0", contents
->bounds().origin().ToString());
210 EXPECT_EQ("-1,0", header
->bounds().origin().ToString());
212 // Scrolling the vertical scrollbar shouldn't effect the header.
213 ASSERT_TRUE(scroll_view
.vertical_scroll_bar());
214 scroll_view
.ScrollToPosition(
215 const_cast<ScrollBar
*>(scroll_view
.vertical_scroll_bar()), 1);
216 EXPECT_EQ("-1,-1", contents
->bounds().origin().ToString());
217 EXPECT_EQ("-1,0", header
->bounds().origin().ToString());
220 // Verifies ScrollRectToVisible() on the child works.
221 TEST(ScrollViewTest
, ScrollRectToVisible
) {
222 ScrollView scroll_view
;
223 CustomView
* contents
= new CustomView
;
224 scroll_view
.SetContents(contents
);
225 contents
->SetPreferredSize(gfx::Size(500, 1000));
227 scroll_view
.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
228 scroll_view
.Layout();
229 EXPECT_EQ("0,0", contents
->bounds().origin().ToString());
231 // Scroll to y=405 height=10, this should make the y position of the content
232 // at (405 + 10) - viewport_height (scroll region bottom aligned).
233 contents
->ScrollRectToVisible(gfx::Rect(0, 405, 10, 10));
234 const int viewport_height
= contents
->parent()->height();
235 EXPECT_EQ(-(415 - viewport_height
), contents
->y());
237 // Scroll to the current y-location and 10x10; should do nothing.
238 contents
->ScrollRectToVisible(gfx::Rect(0, -contents
->y(), 10, 10));
239 EXPECT_EQ(-(415 - viewport_height
), contents
->y());
242 // Verifies ClipHeightTo() uses the height of the content when it is between the
243 // minimum and maximum height values.
244 TEST(ScrollViewTest
, ClipHeightToNormalContentHeight
) {
245 ScrollView scroll_view
;
247 scroll_view
.ClipHeightTo(kMinHeight
, kMaxHeight
);
249 const int kNormalContentHeight
= 75;
250 scroll_view
.SetContents(
251 new views::StaticSizedView(gfx::Size(kWidth
, kNormalContentHeight
)));
253 EXPECT_EQ(gfx::Size(kWidth
, kNormalContentHeight
),
254 scroll_view
.GetPreferredSize());
256 scroll_view
.SizeToPreferredSize();
257 scroll_view
.Layout();
259 EXPECT_EQ(gfx::Size(kWidth
, kNormalContentHeight
),
260 scroll_view
.contents()->size());
261 EXPECT_EQ(gfx::Size(kWidth
, kNormalContentHeight
), scroll_view
.size());
264 // Verifies ClipHeightTo() uses the minimum height when the content is shorter
265 // thamn the minimum height value.
266 TEST(ScrollViewTest
, ClipHeightToShortContentHeight
) {
267 ScrollView scroll_view
;
269 scroll_view
.ClipHeightTo(kMinHeight
, kMaxHeight
);
271 const int kShortContentHeight
= 10;
272 scroll_view
.SetContents(
273 new views::StaticSizedView(gfx::Size(kWidth
, kShortContentHeight
)));
275 EXPECT_EQ(gfx::Size(kWidth
, kMinHeight
), scroll_view
.GetPreferredSize());
277 scroll_view
.SizeToPreferredSize();
278 scroll_view
.Layout();
280 EXPECT_EQ(gfx::Size(kWidth
, kShortContentHeight
),
281 scroll_view
.contents()->size());
282 EXPECT_EQ(gfx::Size(kWidth
, kMinHeight
), scroll_view
.size());
285 // Verifies ClipHeightTo() uses the maximum height when the content is longer
286 // thamn the maximum height value.
287 TEST(ScrollViewTest
, ClipHeightToTallContentHeight
) {
288 ScrollView scroll_view
;
290 // Use a scrollbar that is disabled by default, so the width of the content is
292 scroll_view
.SetVerticalScrollBar(new views::OverlayScrollBar(false));
294 scroll_view
.ClipHeightTo(kMinHeight
, kMaxHeight
);
296 const int kTallContentHeight
= 1000;
297 scroll_view
.SetContents(
298 new views::StaticSizedView(gfx::Size(kWidth
, kTallContentHeight
)));
300 EXPECT_EQ(gfx::Size(kWidth
, kMaxHeight
), scroll_view
.GetPreferredSize());
302 scroll_view
.SizeToPreferredSize();
303 scroll_view
.Layout();
305 EXPECT_EQ(gfx::Size(kWidth
, kTallContentHeight
),
306 scroll_view
.contents()->size());
307 EXPECT_EQ(gfx::Size(kWidth
, kMaxHeight
), scroll_view
.size());
310 // Verifies that when ClipHeightTo() produces a scrollbar, it reduces the width
311 // of the inner content of the ScrollView.
312 TEST(ScrollViewTest
, ClipHeightToScrollbarUsesWidth
) {
313 ScrollView scroll_view
;
315 scroll_view
.ClipHeightTo(kMinHeight
, kMaxHeight
);
317 // Create a view that will be much taller than it is wide.
318 scroll_view
.SetContents(new views::ProportionallySizedView(1000));
320 // Without any width, it will default to 0,0 but be overridden by min height.
321 scroll_view
.SizeToPreferredSize();
322 EXPECT_EQ(gfx::Size(0, kMinHeight
), scroll_view
.GetPreferredSize());
324 gfx::Size
new_size(kWidth
, scroll_view
.GetHeightForWidth(kWidth
));
325 scroll_view
.SetSize(new_size
);
326 scroll_view
.Layout();
328 int scroll_bar_width
= scroll_view
.GetScrollBarWidth();
329 int expected_width
= kWidth
- scroll_bar_width
;
330 EXPECT_EQ(scroll_view
.contents()->size().width(), expected_width
);
331 EXPECT_EQ(scroll_view
.contents()->size().height(), 1000 * expected_width
);
332 EXPECT_EQ(gfx::Size(kWidth
, kMaxHeight
), scroll_view
.size());
335 TEST(ScrollViewTest
, CornerViewVisibility
) {
336 ScrollView scroll_view
;
337 View
* contents
= new View
;
338 scroll_view
.SetContents(contents
);
339 scroll_view
.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
340 View
* corner_view
= scroll_view
.corner_view_
;
342 // Corner view should be visible when both scrollbars are visible.
343 contents
->SetBounds(0, 0, 200, 200);
344 scroll_view
.Layout();
345 EXPECT_EQ(&scroll_view
, corner_view
->parent());
346 EXPECT_TRUE(corner_view
->visible());
348 // Corner view should be aligned to the scrollbars.
349 EXPECT_EQ(scroll_view
.vertical_scroll_bar()->x(), corner_view
->x());
350 EXPECT_EQ(scroll_view
.horizontal_scroll_bar()->y(), corner_view
->y());
351 EXPECT_EQ(scroll_view
.GetScrollBarWidth(), corner_view
->width());
352 EXPECT_EQ(scroll_view
.GetScrollBarHeight(), corner_view
->height());
354 // Corner view should be removed when only the vertical scrollbar is visible.
355 contents
->SetBounds(0, 0, 50, 200);
356 scroll_view
.Layout();
357 EXPECT_FALSE(corner_view
->parent());
359 // ... or when only the horizontal scrollbar is visible.
360 contents
->SetBounds(0, 0, 200, 50);
361 scroll_view
.Layout();
362 EXPECT_FALSE(corner_view
->parent());
364 // ... or when no scrollbar is visible.
365 contents
->SetBounds(0, 0, 50, 50);
366 scroll_view
.Layout();
367 EXPECT_FALSE(corner_view
->parent());
369 // Corner view should reappear when both scrollbars reappear.
370 contents
->SetBounds(0, 0, 200, 200);
371 scroll_view
.Layout();
372 EXPECT_EQ(&scroll_view
, corner_view
->parent());
373 EXPECT_TRUE(corner_view
->visible());