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 TEST_F(GridLayoutTest
, SameSizeColumns
) {
256 SettableSizeView
v1(gfx::Size(50, 20));
257 SettableSizeView
v2(gfx::Size(10, 10));
258 ColumnSet
* c1
= layout
.AddColumnSet(0);
259 c1
->AddColumn(GridLayout::LEADING
, GridLayout::LEADING
,
260 0, GridLayout::USE_PREF
, 0, 0);
261 c1
->AddColumn(GridLayout::LEADING
, GridLayout::LEADING
,
262 0, GridLayout::USE_PREF
, 0, 0);
263 c1
->LinkColumnSizes(0, 1, -1);
264 layout
.StartRow(0, 0);
268 gfx::Size pref
= layout
.GetPreferredSize(&host
);
269 EXPECT_EQ(gfx::Size(100, 20), pref
);
271 host
.SetBounds(0, 0, pref
.width(), pref
.height());
272 layout
.Layout(&host
);
273 ExpectViewBoundsEquals(0, 0, 50, 20, &v1
);
274 ExpectViewBoundsEquals(50, 0, 10, 10, &v2
);
279 TEST_F(GridLayoutTest
, HorizontalResizeTest1
) {
280 SettableSizeView
v1(gfx::Size(50, 20));
281 SettableSizeView
v2(gfx::Size(10, 10));
282 ColumnSet
* c1
= layout
.AddColumnSet(0);
283 c1
->AddColumn(GridLayout::FILL
, GridLayout::LEADING
,
284 1, GridLayout::USE_PREF
, 0, 0);
285 c1
->AddColumn(GridLayout::LEADING
, GridLayout::LEADING
,
286 0, GridLayout::USE_PREF
, 0, 0);
287 layout
.StartRow(0, 0);
291 host
.SetBounds(0, 0, 110, 20);
292 layout
.Layout(&host
);
293 ExpectViewBoundsEquals(0, 0, 100, 20, &v1
);
294 ExpectViewBoundsEquals(100, 0, 10, 10, &v2
);
299 TEST_F(GridLayoutTest
, HorizontalResizeTest2
) {
300 SettableSizeView
v1(gfx::Size(50, 20));
301 SettableSizeView
v2(gfx::Size(10, 10));
302 ColumnSet
* c1
= layout
.AddColumnSet(0);
303 c1
->AddColumn(GridLayout::FILL
, GridLayout::LEADING
,
304 1, GridLayout::USE_PREF
, 0, 0);
305 c1
->AddColumn(GridLayout::TRAILING
, GridLayout::LEADING
,
306 1, GridLayout::USE_PREF
, 0, 0);
307 layout
.StartRow(0, 0);
311 host
.SetBounds(0, 0, 120, 20);
312 layout
.Layout(&host
);
313 ExpectViewBoundsEquals(0, 0, 80, 20, &v1
);
314 ExpectViewBoundsEquals(110, 0, 10, 10, &v2
);
319 TEST_F(GridLayoutTest
, TestVerticalResize1
) {
320 SettableSizeView
v1(gfx::Size(50, 20));
321 SettableSizeView
v2(gfx::Size(10, 10));
322 ColumnSet
* c1
= layout
.AddColumnSet(0);
323 c1
->AddColumn(GridLayout::FILL
, GridLayout::FILL
,
324 1, GridLayout::USE_PREF
, 0, 0);
325 layout
.StartRow(1, 0);
327 layout
.StartRow(0, 0);
331 EXPECT_EQ(gfx::Size(50, 30), pref
);
333 host
.SetBounds(0, 0, 50, 100);
334 layout
.Layout(&host
);
335 ExpectViewBoundsEquals(0, 0, 50, 90, &v1
);
336 ExpectViewBoundsEquals(0, 90, 50, 10, &v2
);
341 TEST_F(GridLayoutTest
, Insets
) {
342 SettableSizeView
v1(gfx::Size(10, 20));
343 ColumnSet
* c1
= layout
.AddColumnSet(0);
344 layout
.SetInsets(1, 2, 3, 4);
345 c1
->AddColumn(GridLayout::LEADING
, GridLayout::LEADING
,
346 0, GridLayout::USE_PREF
, 0, 0);
347 layout
.StartRow(0, 0);
351 EXPECT_EQ(gfx::Size(16, 24), pref
);
353 host
.SetBounds(0, 0, pref
.width(), pref
.height());
354 layout
.Layout(&host
);
355 ExpectViewBoundsEquals(2, 1, 10, 20, &v1
);
360 TEST_F(GridLayoutTest
, FixedSize
) {
361 layout
.SetInsets(2, 2, 2, 2);
363 ColumnSet
* set
= layout
.AddColumnSet(0);
365 int column_count
= 4;
366 int title_width
= 100;
369 int pref_height
= 20;
371 for (int i
= 0; i
< column_count
; ++i
) {
372 set
->AddColumn(GridLayout::CENTER
,
380 for (int row
= 0; row
< row_count
; ++row
) {
381 layout
.StartRow(0, 0);
382 for (int col
= 0; col
< column_count
; ++col
) {
383 layout
.AddView(new SettableSizeView(gfx::Size(pref_width
, pref_height
)));
387 layout
.Layout(&host
);
389 for (int i
= 0; i
< column_count
; ++i
) {
390 for (int row
= 0; row
< row_count
; ++row
) {
391 View
* view
= host
.child_at(row
* column_count
+ i
);
392 ExpectViewBoundsEquals(
393 2 + title_width
* i
+ (title_width
- pref_width
) / 2,
394 2 + pref_height
* row
,
401 EXPECT_EQ(gfx::Size(column_count
* title_width
+ 4,
402 row_count
* pref_height
+ 4), pref
);
405 TEST_F(GridLayoutTest
, RowSpanWithPaddingRow
) {
406 ColumnSet
* set
= layout
.AddColumnSet(0);
408 set
->AddColumn(GridLayout::CENTER
,
415 layout
.StartRow(0, 0);
416 layout
.AddView(new SettableSizeView(gfx::Size(10, 10)), 1, 2);
417 layout
.AddPaddingRow(0, 10);
420 TEST_F(GridLayoutTest
, RowSpan
) {
421 ColumnSet
* set
= layout
.AddColumnSet(0);
423 set
->AddColumn(GridLayout::LEADING
,
426 GridLayout::USE_PREF
,
429 set
->AddColumn(GridLayout::LEADING
,
432 GridLayout::USE_PREF
,
436 layout
.StartRow(0, 0);
437 layout
.AddView(new SettableSizeView(gfx::Size(20, 10)));
438 layout
.AddView(new SettableSizeView(gfx::Size(20, 40)), 1, 2);
439 layout
.StartRow(1, 0);
440 View
* s3
= new SettableSizeView(gfx::Size(20, 10));
444 EXPECT_EQ(gfx::Size(40, 40), pref
);
446 host
.SetBounds(0, 0, pref
.width(), pref
.height());
447 layout
.Layout(&host
);
448 ExpectViewBoundsEquals(0, 10, 20, 10, s3
);
451 TEST_F(GridLayoutTest
, RowSpan2
) {
452 ColumnSet
* set
= layout
.AddColumnSet(0);
454 set
->AddColumn(GridLayout::LEADING
, GridLayout::LEADING
,
455 0, GridLayout::USE_PREF
, 0, 0);
456 set
->AddColumn(GridLayout::LEADING
, GridLayout::LEADING
,
457 0,GridLayout::USE_PREF
, 0, 0);
459 layout
.StartRow(0, 0);
460 layout
.AddView(new SettableSizeView(gfx::Size(20, 20)));
461 View
* s3
= new SettableSizeView(gfx::Size(64, 64));
462 layout
.AddView(s3
, 1, 3);
464 layout
.AddPaddingRow(0, 10);
466 layout
.StartRow(0, 0);
467 layout
.AddView(new SettableSizeView(gfx::Size(10, 20)));
470 EXPECT_EQ(gfx::Size(84, 64), pref
);
472 host
.SetBounds(0, 0, pref
.width(), pref
.height());
473 layout
.Layout(&host
);
474 ExpectViewBoundsEquals(20, 0, 64, 64, s3
);
477 TEST_F(GridLayoutTest
, FixedViewWidth
) {
478 ColumnSet
* set
= layout
.AddColumnSet(0);
480 set
->AddColumn(GridLayout::LEADING
, GridLayout::LEADING
,
481 0, GridLayout::USE_PREF
, 0, 0);
482 set
->AddColumn(GridLayout::LEADING
, GridLayout::LEADING
,
483 0,GridLayout::USE_PREF
, 0, 0);
485 layout
.StartRow(0, 0);
486 View
* view
= new SettableSizeView(gfx::Size(30, 40));
487 layout
.AddView(view
, 1, 1, GridLayout::LEADING
, GridLayout::LEADING
, 10, 0);
490 EXPECT_EQ(10, pref
.width());
491 EXPECT_EQ(40, pref
.height());
493 host
.SetBounds(0, 0, pref
.width(), pref
.height());
494 layout
.Layout(&host
);
495 ExpectViewBoundsEquals(0, 0, 10, 40, view
);
498 TEST_F(GridLayoutTest
, FixedViewHeight
) {
499 ColumnSet
* set
= layout
.AddColumnSet(0);
501 set
->AddColumn(GridLayout::LEADING
, GridLayout::LEADING
,
502 0, GridLayout::USE_PREF
, 0, 0);
503 set
->AddColumn(GridLayout::LEADING
, GridLayout::LEADING
,
504 0,GridLayout::USE_PREF
, 0, 0);
506 layout
.StartRow(0, 0);
507 View
* view
= new SettableSizeView(gfx::Size(30, 40));
508 layout
.AddView(view
, 1, 1, GridLayout::LEADING
, GridLayout::LEADING
, 0, 10);
511 EXPECT_EQ(30, pref
.width());
512 EXPECT_EQ(10, pref
.height());
514 host
.SetBounds(0, 0, pref
.width(), pref
.height());
515 layout
.Layout(&host
);
516 ExpectViewBoundsEquals(0, 0, 30, 10, view
);
519 // Make sure that for views that span columns the underlying columns are resized
520 // based on the resize percent of the column.
521 TEST_F(GridLayoutTest
, ColumnSpanResizing
) {
522 ColumnSet
* set
= layout
.AddColumnSet(0);
524 set
->AddColumn(GridLayout::FILL
, GridLayout::CENTER
,
525 2, GridLayout::USE_PREF
, 0, 0);
526 set
->AddColumn(GridLayout::FILL
, GridLayout::CENTER
,
527 4, GridLayout::USE_PREF
, 0, 0);
529 layout
.StartRow(0, 0);
530 // span_view spans two columns and is twice as big the views added below.
531 View
* span_view
= new SettableSizeView(gfx::Size(12, 40));
532 layout
.AddView(span_view
, 2, 1, GridLayout::LEADING
, GridLayout::LEADING
);
534 layout
.StartRow(0, 0);
535 View
* view1
= new SettableSizeView(gfx::Size(2, 40));
536 View
* view2
= new SettableSizeView(gfx::Size(4, 40));
537 layout
.AddView(view1
);
538 layout
.AddView(view2
);
540 host
.SetBounds(0, 0, 12, 80);
541 layout
.Layout(&host
);
543 ExpectViewBoundsEquals(0, 0, 12, 40, span_view
);
545 // view1 should be 4 pixels wide
546 // column_pref + (remaining_width * column_resize / total_column_resize) =
548 ExpectViewBoundsEquals(0, 40, 4, 40, view1
);
550 // And view2 should be 8 pixels wide:
552 ExpectViewBoundsEquals(4, 40, 8, 40, view2
);
555 // Check that GetPreferredSize() takes resizing of columns into account when
556 // there is additional space in the case we have column sets of different
558 TEST_F(GridLayoutTest
, ColumnResizingOnGetPreferredSize
) {
559 ColumnSet
* set
= layout
.AddColumnSet(0);
560 set
->AddColumn(GridLayout::FILL
, GridLayout::CENTER
,
561 1, GridLayout::USE_PREF
, 0, 0);
563 set
= layout
.AddColumnSet(1);
564 set
->AddColumn(GridLayout::FILL
, GridLayout::CENTER
,
565 1, GridLayout::USE_PREF
, 0, 0);
567 set
= layout
.AddColumnSet(2);
568 set
->AddColumn(GridLayout::FILL
, GridLayout::CENTER
,
569 1, GridLayout::USE_PREF
, 0, 0);
571 // Make a row containing a flexible view that trades width for height.
572 layout
.StartRow(0, 0);
573 View
* view1
= new FlexibleView(100);
574 layout
.AddView(view1
, 1, 1, GridLayout::FILL
, GridLayout::LEADING
);
576 // The second row contains a view of fixed size that will enforce a column
577 // width of 20 pixels.
578 layout
.StartRow(0, 1);
579 View
* view2
= new SettableSizeView(gfx::Size(20, 20));
580 layout
.AddView(view2
, 1, 1, GridLayout::FILL
, GridLayout::LEADING
);
582 // Add another flexible view in row three in order to ensure column set
583 // ordering doesn't influence sizing behaviour.
584 layout
.StartRow(0, 2);
585 View
* view3
= new FlexibleView(40);
586 layout
.AddView(view3
, 1, 1, GridLayout::FILL
, GridLayout::LEADING
);
588 // We expect a height of 50: 30 from the variable width view in the first row
589 // plus 20 from the statically sized view in the second row. The flexible
590 // view in the third row should contribute no height.
591 EXPECT_EQ(gfx::Size(20, 50), layout
.GetPreferredSize(&host
));
594 TEST_F(GridLayoutTest
, MinimumPreferredSize
) {
595 SettableSizeView
v1(gfx::Size(10, 20));
596 ColumnSet
* set
= layout
.AddColumnSet(0);
597 set
->AddColumn(GridLayout::FILL
, GridLayout::FILL
,
598 0, GridLayout::USE_PREF
, 0, 0);
599 layout
.StartRow(0, 0);
603 EXPECT_EQ(gfx::Size(10, 20), pref
);
605 layout
.set_minimum_size(gfx::Size(40, 40));
607 EXPECT_EQ(gfx::Size(40, 40), pref
);