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/layout/grid_layout.h"
7 #include "base/compiler_specific.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "ui/views/view.h"
13 void ExpectViewBoundsEquals(int x, int y, int w, int h,
15 EXPECT_EQ(x, view->x());
16 EXPECT_EQ(y, view->y());
17 EXPECT_EQ(w, view->width());
18 EXPECT_EQ(h, view->height());
21 class SettableSizeView : public View {
23 explicit SettableSizeView(const gfx::Size& pref) {
27 virtual gfx::Size GetPreferredSize() OVERRIDE {
35 // A view with fixed circumference that trades height for width.
36 class FlexibleView : public View {
38 explicit FlexibleView(int circumference) {
39 circumference_ = circumference;
42 virtual gfx::Size GetPreferredSize() OVERRIDE {
43 return gfx::Size(0, circumference_ / 2);
46 virtual int GetHeightForWidth(int width) OVERRIDE {
47 return std::max(0, circumference_ / 2 - width);
54 class GridLayoutTest : public testing::Test {
56 GridLayoutTest() : layout(&host) {}
59 for (int i = host.child_count() - 1; i >= 0; i--)
60 host.RemoveChildView(host.child_at(i));
63 void GetPreferredSize() {
64 pref = layout.GetPreferredSize(&host);
73 class GridLayoutAlignmentTest : public testing::Test {
75 GridLayoutAlignmentTest()
76 : v1(gfx::Size(10, 20)),
80 for (int i = host.child_count() - 1; i >= 0; i--)
81 host.RemoveChildView(host.child_at(i));
84 void TestAlignment(GridLayout::Alignment alignment, gfx::Rect* bounds) {
85 ColumnSet* c1 = layout.AddColumnSet(0);
86 c1->AddColumn(alignment, alignment, 1, GridLayout::USE_PREF, 0, 0);
87 layout.StartRow(1, 0);
89 gfx::Size pref = layout.GetPreferredSize(&host);
90 EXPECT_EQ(gfx::Size(10, 20), pref);
91 host.SetBounds(0, 0, 100, 100);
93 *bounds = v1.bounds();
102 TEST_F(GridLayoutAlignmentTest, Fill) {
104 TestAlignment(GridLayout::FILL, &bounds);
105 EXPECT_EQ(gfx::Rect(0, 0, 100, 100), bounds);
108 TEST_F(GridLayoutAlignmentTest, Leading) {
110 TestAlignment(GridLayout::LEADING, &bounds);
111 EXPECT_EQ(gfx::Rect(0, 0, 10, 20), bounds);
114 TEST_F(GridLayoutAlignmentTest, Center) {
116 TestAlignment(GridLayout::CENTER, &bounds);
117 EXPECT_EQ(gfx::Rect(45, 40, 10, 20), bounds);
120 TEST_F(GridLayoutAlignmentTest, Trailing) {
122 TestAlignment(GridLayout::TRAILING, &bounds);
123 EXPECT_EQ(gfx::Rect(90, 80, 10, 20), bounds);
126 TEST_F(GridLayoutTest, TwoColumns) {
127 SettableSizeView v1(gfx::Size(10, 20));
128 SettableSizeView v2(gfx::Size(20, 20));
129 ColumnSet* c1 = layout.AddColumnSet(0);
130 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
131 0, GridLayout::USE_PREF, 0, 0);
132 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
133 0, GridLayout::USE_PREF, 0, 0);
134 layout.StartRow(0, 0);
139 EXPECT_EQ(gfx::Size(30, 20), pref);
141 host.SetBounds(0, 0, pref.width(), pref.height());
142 layout.Layout(&host);
143 ExpectViewBoundsEquals(0, 0, 10, 20, &v1);
144 ExpectViewBoundsEquals(10, 0, 20, 20, &v2);
149 TEST_F(GridLayoutTest, ColSpan1) {
150 SettableSizeView v1(gfx::Size(100, 20));
151 SettableSizeView v2(gfx::Size(10, 40));
152 ColumnSet* c1 = layout.AddColumnSet(0);
153 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
154 0, GridLayout::USE_PREF, 0, 0);
155 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
156 1, GridLayout::USE_PREF, 0, 0);
157 layout.StartRow(0, 0);
158 layout.AddView(&v1, 2, 1);
159 layout.StartRow(0, 0);
163 EXPECT_EQ(gfx::Size(100, 60), pref);
165 host.SetBounds(0, 0, pref.width(), pref.height());
166 layout.Layout(&host);
167 ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
168 ExpectViewBoundsEquals(0, 20, 10, 40, &v2);
173 TEST_F(GridLayoutTest, ColSpan2) {
174 SettableSizeView v1(gfx::Size(100, 20));
175 SettableSizeView v2(gfx::Size(10, 20));
176 ColumnSet* c1 = layout.AddColumnSet(0);
177 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
178 1, GridLayout::USE_PREF, 0, 0);
179 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
180 0, GridLayout::USE_PREF, 0, 0);
181 layout.StartRow(0, 0);
182 layout.AddView(&v1, 2, 1);
183 layout.StartRow(0, 0);
184 layout.SkipColumns(1);
188 EXPECT_EQ(gfx::Size(100, 40), pref);
190 host.SetBounds(0, 0, pref.width(), pref.height());
191 layout.Layout(&host);
192 ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
193 ExpectViewBoundsEquals(90, 20, 10, 20, &v2);
198 TEST_F(GridLayoutTest, ColSpan3) {
199 SettableSizeView v1(gfx::Size(100, 20));
200 SettableSizeView v2(gfx::Size(10, 20));
201 SettableSizeView v3(gfx::Size(10, 20));
202 ColumnSet* c1 = layout.AddColumnSet(0);
203 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
204 0, GridLayout::USE_PREF, 0, 0);
205 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
206 0, GridLayout::USE_PREF, 0, 0);
207 layout.StartRow(0, 0);
208 layout.AddView(&v1, 2, 1);
209 layout.StartRow(0, 0);
214 EXPECT_EQ(gfx::Size(100, 40), pref);
216 host.SetBounds(0, 0, pref.width(), pref.height());
217 layout.Layout(&host);
218 ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
219 ExpectViewBoundsEquals(0, 20, 10, 20, &v2);
220 ExpectViewBoundsEquals(50, 20, 10, 20, &v3);
226 TEST_F(GridLayoutTest, ColSpan4) {
227 ColumnSet* set = layout.AddColumnSet(0);
229 set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
230 GridLayout::USE_PREF, 0, 0);
231 set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
232 GridLayout::USE_PREF, 0, 0);
234 SettableSizeView v1(gfx::Size(10, 10));
235 SettableSizeView v2(gfx::Size(10, 10));
236 SettableSizeView v3(gfx::Size(25, 20));
237 layout.StartRow(0, 0);
240 layout.StartRow(0, 0);
241 layout.AddView(&v3, 2, 1);
244 EXPECT_EQ(gfx::Size(25, 30), pref);
246 host.SetBounds(0, 0, pref.width(), pref.height());
247 layout.Layout(&host);
248 ExpectViewBoundsEquals(0, 0, 10, 10, &v1);
249 ExpectViewBoundsEquals(12, 0, 10, 10, &v2);
250 ExpectViewBoundsEquals(0, 10, 25, 20, &v3);
255 // Verifies the sizing of a view that doesn't start in the first column
256 // and has a column span > 1 (crbug.com/254092).
257 TEST_F(GridLayoutTest, ColSpanStartSecondColumn) {
258 ColumnSet* set = layout.AddColumnSet(0);
260 set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
261 GridLayout::USE_PREF, 0, 0);
262 set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
263 GridLayout::USE_PREF, 0, 0);
264 set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
265 GridLayout::FIXED, 10, 0);
267 SettableSizeView v1(gfx::Size(10, 10));
268 SettableSizeView v2(gfx::Size(20, 10));
270 layout.StartRow(0, 0);
272 layout.AddView(&v2, 2, 1);
275 EXPECT_EQ(gfx::Size(30, 10), pref);
277 host.SetBounds(0, 0, pref.width(), pref.height());
278 layout.Layout(&host);
279 ExpectViewBoundsEquals(0, 0, 10, 10, &v1);
280 ExpectViewBoundsEquals(10, 0, 20, 10, &v2);
285 TEST_F(GridLayoutTest, SameSizeColumns) {
286 SettableSizeView v1(gfx::Size(50, 20));
287 SettableSizeView v2(gfx::Size(10, 10));
288 ColumnSet* c1 = layout.AddColumnSet(0);
289 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
290 0, GridLayout::USE_PREF, 0, 0);
291 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
292 0, GridLayout::USE_PREF, 0, 0);
293 c1->LinkColumnSizes(0, 1, -1);
294 layout.StartRow(0, 0);
298 gfx::Size pref = layout.GetPreferredSize(&host);
299 EXPECT_EQ(gfx::Size(100, 20), pref);
301 host.SetBounds(0, 0, pref.width(), pref.height());
302 layout.Layout(&host);
303 ExpectViewBoundsEquals(0, 0, 50, 20, &v1);
304 ExpectViewBoundsEquals(50, 0, 10, 10, &v2);
309 TEST_F(GridLayoutTest, HorizontalResizeTest1) {
310 SettableSizeView v1(gfx::Size(50, 20));
311 SettableSizeView v2(gfx::Size(10, 10));
312 ColumnSet* c1 = layout.AddColumnSet(0);
313 c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
314 1, GridLayout::USE_PREF, 0, 0);
315 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
316 0, GridLayout::USE_PREF, 0, 0);
317 layout.StartRow(0, 0);
321 host.SetBounds(0, 0, 110, 20);
322 layout.Layout(&host);
323 ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
324 ExpectViewBoundsEquals(100, 0, 10, 10, &v2);
329 TEST_F(GridLayoutTest, HorizontalResizeTest2) {
330 SettableSizeView v1(gfx::Size(50, 20));
331 SettableSizeView v2(gfx::Size(10, 10));
332 ColumnSet* c1 = layout.AddColumnSet(0);
333 c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
334 1, GridLayout::USE_PREF, 0, 0);
335 c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING,
336 1, GridLayout::USE_PREF, 0, 0);
337 layout.StartRow(0, 0);
341 host.SetBounds(0, 0, 120, 20);
342 layout.Layout(&host);
343 ExpectViewBoundsEquals(0, 0, 80, 20, &v1);
344 ExpectViewBoundsEquals(110, 0, 10, 10, &v2);
349 TEST_F(GridLayoutTest, TestVerticalResize1) {
350 SettableSizeView v1(gfx::Size(50, 20));
351 SettableSizeView v2(gfx::Size(10, 10));
352 ColumnSet* c1 = layout.AddColumnSet(0);
353 c1->AddColumn(GridLayout::FILL, GridLayout::FILL,
354 1, GridLayout::USE_PREF, 0, 0);
355 layout.StartRow(1, 0);
357 layout.StartRow(0, 0);
361 EXPECT_EQ(gfx::Size(50, 30), pref);
363 host.SetBounds(0, 0, 50, 100);
364 layout.Layout(&host);
365 ExpectViewBoundsEquals(0, 0, 50, 90, &v1);
366 ExpectViewBoundsEquals(0, 90, 50, 10, &v2);
371 TEST_F(GridLayoutTest, Insets) {
372 SettableSizeView v1(gfx::Size(10, 20));
373 ColumnSet* c1 = layout.AddColumnSet(0);
374 layout.SetInsets(1, 2, 3, 4);
375 c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
376 0, GridLayout::USE_PREF, 0, 0);
377 layout.StartRow(0, 0);
381 EXPECT_EQ(gfx::Size(16, 24), pref);
383 host.SetBounds(0, 0, pref.width(), pref.height());
384 layout.Layout(&host);
385 ExpectViewBoundsEquals(2, 1, 10, 20, &v1);
390 TEST_F(GridLayoutTest, FixedSize) {
391 layout.SetInsets(2, 2, 2, 2);
393 ColumnSet* set = layout.AddColumnSet(0);
395 int column_count = 4;
396 int title_width = 100;
399 int pref_height = 20;
401 for (int i = 0; i < column_count; ++i) {
402 set->AddColumn(GridLayout::CENTER,
410 for (int row = 0; row < row_count; ++row) {
411 layout.StartRow(0, 0);
412 for (int col = 0; col < column_count; ++col) {
413 layout.AddView(new SettableSizeView(gfx::Size(pref_width, pref_height)));
417 layout.Layout(&host);
419 for (int i = 0; i < column_count; ++i) {
420 for (int row = 0; row < row_count; ++row) {
421 View* view = host.child_at(row * column_count + i);
422 ExpectViewBoundsEquals(
423 2 + title_width * i + (title_width - pref_width) / 2,
424 2 + pref_height * row,
431 EXPECT_EQ(gfx::Size(column_count * title_width + 4,
432 row_count * pref_height + 4), pref);
435 TEST_F(GridLayoutTest, RowSpanWithPaddingRow) {
436 ColumnSet* set = layout.AddColumnSet(0);
438 set->AddColumn(GridLayout::CENTER,
445 layout.StartRow(0, 0);
446 layout.AddView(new SettableSizeView(gfx::Size(10, 10)), 1, 2);
447 layout.AddPaddingRow(0, 10);
450 TEST_F(GridLayoutTest, RowSpan) {
451 ColumnSet* set = layout.AddColumnSet(0);
453 set->AddColumn(GridLayout::LEADING,
456 GridLayout::USE_PREF,
459 set->AddColumn(GridLayout::LEADING,
462 GridLayout::USE_PREF,
466 layout.StartRow(0, 0);
467 layout.AddView(new SettableSizeView(gfx::Size(20, 10)));
468 layout.AddView(new SettableSizeView(gfx::Size(20, 40)), 1, 2);
469 layout.StartRow(1, 0);
470 View* s3 = new SettableSizeView(gfx::Size(20, 10));
474 EXPECT_EQ(gfx::Size(40, 40), pref);
476 host.SetBounds(0, 0, pref.width(), pref.height());
477 layout.Layout(&host);
478 ExpectViewBoundsEquals(0, 10, 20, 10, s3);
481 TEST_F(GridLayoutTest, RowSpan2) {
482 ColumnSet* set = layout.AddColumnSet(0);
484 set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
485 0, GridLayout::USE_PREF, 0, 0);
486 set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
487 0,GridLayout::USE_PREF, 0, 0);
489 layout.StartRow(0, 0);
490 layout.AddView(new SettableSizeView(gfx::Size(20, 20)));
491 View* s3 = new SettableSizeView(gfx::Size(64, 64));
492 layout.AddView(s3, 1, 3);
494 layout.AddPaddingRow(0, 10);
496 layout.StartRow(0, 0);
497 layout.AddView(new SettableSizeView(gfx::Size(10, 20)));
500 EXPECT_EQ(gfx::Size(84, 64), pref);
502 host.SetBounds(0, 0, pref.width(), pref.height());
503 layout.Layout(&host);
504 ExpectViewBoundsEquals(20, 0, 64, 64, s3);
507 TEST_F(GridLayoutTest, FixedViewWidth) {
508 ColumnSet* set = layout.AddColumnSet(0);
510 set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
511 0, GridLayout::USE_PREF, 0, 0);
512 set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
513 0,GridLayout::USE_PREF, 0, 0);
515 layout.StartRow(0, 0);
516 View* view = new SettableSizeView(gfx::Size(30, 40));
517 layout.AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 10, 0);
520 EXPECT_EQ(10, pref.width());
521 EXPECT_EQ(40, pref.height());
523 host.SetBounds(0, 0, pref.width(), pref.height());
524 layout.Layout(&host);
525 ExpectViewBoundsEquals(0, 0, 10, 40, view);
528 TEST_F(GridLayoutTest, FixedViewHeight) {
529 ColumnSet* set = layout.AddColumnSet(0);
531 set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
532 0, GridLayout::USE_PREF, 0, 0);
533 set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
534 0,GridLayout::USE_PREF, 0, 0);
536 layout.StartRow(0, 0);
537 View* view = new SettableSizeView(gfx::Size(30, 40));
538 layout.AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 0, 10);
541 EXPECT_EQ(30, pref.width());
542 EXPECT_EQ(10, pref.height());
544 host.SetBounds(0, 0, pref.width(), pref.height());
545 layout.Layout(&host);
546 ExpectViewBoundsEquals(0, 0, 30, 10, view);
549 // Make sure that for views that span columns the underlying columns are resized
550 // based on the resize percent of the column.
551 TEST_F(GridLayoutTest, ColumnSpanResizing) {
552 ColumnSet* set = layout.AddColumnSet(0);
554 set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
555 2, GridLayout::USE_PREF, 0, 0);
556 set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
557 4, GridLayout::USE_PREF, 0, 0);
559 layout.StartRow(0, 0);
560 // span_view spans two columns and is twice as big the views added below.
561 View* span_view = new SettableSizeView(gfx::Size(12, 40));
562 layout.AddView(span_view, 2, 1, GridLayout::LEADING, GridLayout::LEADING);
564 layout.StartRow(0, 0);
565 View* view1 = new SettableSizeView(gfx::Size(2, 40));
566 View* view2 = new SettableSizeView(gfx::Size(4, 40));
567 layout.AddView(view1);
568 layout.AddView(view2);
570 host.SetBounds(0, 0, 12, 80);
571 layout.Layout(&host);
573 ExpectViewBoundsEquals(0, 0, 12, 40, span_view);
575 // view1 should be 4 pixels wide
576 // column_pref + (remaining_width * column_resize / total_column_resize) =
578 ExpectViewBoundsEquals(0, 40, 4, 40, view1);
580 // And view2 should be 8 pixels wide:
582 ExpectViewBoundsEquals(4, 40, 8, 40, view2);
585 // Check that GetPreferredSize() takes resizing of columns into account when
586 // there is additional space in the case we have column sets of different
588 TEST_F(GridLayoutTest, ColumnResizingOnGetPreferredSize) {
589 ColumnSet* set = layout.AddColumnSet(0);
590 set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
591 1, GridLayout::USE_PREF, 0, 0);
593 set = layout.AddColumnSet(1);
594 set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
595 1, GridLayout::USE_PREF, 0, 0);
597 set = layout.AddColumnSet(2);
598 set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
599 1, GridLayout::USE_PREF, 0, 0);
601 // Make a row containing a flexible view that trades width for height.
602 layout.StartRow(0, 0);
603 View* view1 = new FlexibleView(100);
604 layout.AddView(view1, 1, 1, GridLayout::FILL, GridLayout::LEADING);
606 // The second row contains a view of fixed size that will enforce a column
607 // width of 20 pixels.
608 layout.StartRow(0, 1);
609 View* view2 = new SettableSizeView(gfx::Size(20, 20));
610 layout.AddView(view2, 1, 1, GridLayout::FILL, GridLayout::LEADING);
612 // Add another flexible view in row three in order to ensure column set
613 // ordering doesn't influence sizing behaviour.
614 layout.StartRow(0, 2);
615 View* view3 = new FlexibleView(40);
616 layout.AddView(view3, 1, 1, GridLayout::FILL, GridLayout::LEADING);
618 // We expect a height of 50: 30 from the variable width view in the first row
619 // plus 20 from the statically sized view in the second row. The flexible
620 // view in the third row should contribute no height.
621 EXPECT_EQ(gfx::Size(20, 50), layout.GetPreferredSize(&host));
624 TEST_F(GridLayoutTest, MinimumPreferredSize) {
625 SettableSizeView v1(gfx::Size(10, 20));
626 ColumnSet* set = layout.AddColumnSet(0);
627 set->AddColumn(GridLayout::FILL, GridLayout::FILL,
628 0, GridLayout::USE_PREF, 0, 0);
629 layout.StartRow(0, 0);
633 EXPECT_EQ(gfx::Size(10, 20), pref);
635 layout.set_minimum_size(gfx::Size(40, 40));
637 EXPECT_EQ(gfx::Size(40, 40), pref);