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