blob: 8b4dc47fba7d63c2072561bab1aed1185982fbdd [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/layout/grid_layout.h"
#include <memory>
#include <utility>
#include "base/compiler_specific.h"
#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/border.h"
#include "ui/views/test/platform_test_helper.h"
#include "ui/views/view.h"
namespace views {
void ExpectViewBoundsEquals(int x, int y, int w, int h,
const View* view) {
EXPECT_EQ(x, view->x());
EXPECT_EQ(y, view->y());
EXPECT_EQ(w, view->width());
EXPECT_EQ(h, view->height());
}
std::unique_ptr<View> CreateSizedView(const gfx::Size& size) {
auto view = std::make_unique<View>();
view->SetPreferredSize(size);
return view;
}
// View that lets you set the minimum size.
class MinSizeView : public View {
public:
explicit MinSizeView(const gfx::Size& min_size) : min_size_(min_size) {}
~MinSizeView() override = default;
// View:
gfx::Size GetMinimumSize() const override { return min_size_; }
private:
const gfx::Size min_size_;
DISALLOW_COPY_AND_ASSIGN(MinSizeView);
};
std::unique_ptr<MinSizeView> CreateViewWithMinAndPref(const gfx::Size& min,
const gfx::Size& pref) {
auto view = std::make_unique<MinSizeView>(min);
view->SetPreferredSize(pref);
return view;
}
// A test view that wants to alter its preferred size and re-layout when it gets
// added to the View hierarchy.
class LayoutOnAddView : public View {
public:
LayoutOnAddView() { SetPreferredSize(gfx::Size(10, 10)); }
void set_target_size(const gfx::Size& target_size) {
target_size_ = target_size;
}
// View:
void ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) override {
if (GetPreferredSize() == target_size_)
return;
// Contrive a realistic thing that a View might what to do, but which would
// break the layout machinery. Note an override of OnThemeChanged()
// would be more compelling, but there is no Widget in this test harness.
SetPreferredSize(target_size_);
PreferredSizeChanged();
parent()->Layout();
}
private:
gfx::Size target_size_;
DISALLOW_COPY_AND_ASSIGN(LayoutOnAddView);
};
// A view with fixed circumference that trades height for width.
class FlexibleView : public View {
public:
explicit FlexibleView(int circumference) {
circumference_ = circumference;
}
gfx::Size CalculatePreferredSize() const override {
return gfx::Size(0, circumference_ / 2);
}
int GetHeightForWidth(int width) const override {
return std::max(0, circumference_ / 2 - width);
}
private:
int circumference_;
};
class GridLayoutTest : public testing::Test {
public:
GridLayoutTest() : host_(std::make_unique<View>()) {
layout_ = host_->SetLayoutManager(std::make_unique<views::GridLayout>());
}
gfx::Size GetPreferredSize() {
return layout_->GetPreferredSize(host_.get());
}
View* host() { return host_.get(); }
GridLayout* layout() { return layout_; }
private:
std::unique_ptr<View> host_;
GridLayout* layout_;
};
class GridLayoutAlignmentTest : public testing::Test {
public:
GridLayoutAlignmentTest() : host_(std::make_unique<View>()) {
layout_ = host_->SetLayoutManager(std::make_unique<views::GridLayout>());
}
void TestAlignment(GridLayout::Alignment alignment, gfx::Rect* bounds) {
auto v1 = std::make_unique<View>();
v1->SetPreferredSize(gfx::Size(10, 20));
ColumnSet* c1 = layout_->AddColumnSet(0);
c1->AddColumn(alignment, alignment, 1, GridLayout::USE_PREF, 0, 0);
layout_->StartRow(1, 0);
auto* v1_ptr = layout_->AddView(std::move(v1));
gfx::Size pref = layout_->GetPreferredSize(host_.get());
EXPECT_EQ(gfx::Size(10, 20), pref);
host_->SetBounds(0, 0, 100, 100);
layout_->Layout(host_.get());
*bounds = v1_ptr->bounds();
}
View* host() { return host_.get(); }
GridLayout* layout() { return layout_; }
private:
std::unique_ptr<View> host_;
GridLayout* layout_;
};
TEST_F(GridLayoutAlignmentTest, Fill) {
gfx::Rect bounds;
TestAlignment(GridLayout::FILL, &bounds);
EXPECT_EQ(gfx::Rect(0, 0, 100, 100), bounds);
}
TEST_F(GridLayoutAlignmentTest, Leading) {
gfx::Rect bounds;
TestAlignment(GridLayout::LEADING, &bounds);
EXPECT_EQ(gfx::Rect(0, 0, 10, 20), bounds);
}
TEST_F(GridLayoutAlignmentTest, Center) {
gfx::Rect bounds;
TestAlignment(GridLayout::CENTER, &bounds);
EXPECT_EQ(gfx::Rect(45, 40, 10, 20), bounds);
}
TEST_F(GridLayoutAlignmentTest, Trailing) {
gfx::Rect bounds;
TestAlignment(GridLayout::TRAILING, &bounds);
EXPECT_EQ(gfx::Rect(90, 80, 10, 20), bounds);
}
TEST_F(GridLayoutTest, TwoColumns) {
auto v1 = CreateSizedView(gfx::Size(10, 20));
auto v2 = CreateSizedView(gfx::Size(20, 20));
ColumnSet* c1 = layout()->AddColumnSet(0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* v1_ptr = layout()->AddView(std::move(v1));
auto* v2_ptr = layout()->AddView(std::move(v2));
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(30, 20), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 10, 20, v1_ptr);
ExpectViewBoundsEquals(10, 0, 20, 20, v2_ptr);
}
// Test linked column sizes, and the column size limit.
TEST_F(GridLayoutTest, LinkedSizes) {
ColumnSet* c1 = layout()->AddColumnSet(0);
// Fill widths.
c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, GridLayout::USE_PREF,
0, 0);
c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, GridLayout::USE_PREF,
0, 0);
c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, GridLayout::USE_PREF,
0, 0);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(20, 20)));
auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(0, 20)));
// Link all the columns.
c1->LinkColumnSizes(0, 1, 2, -1);
gfx::Size pref = GetPreferredSize();
// |v1| and |v3| should obtain the same width as |v2|, since |v2| is largest.
pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(20 + 20 + 20, 20), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 20, 20, v1);
ExpectViewBoundsEquals(20, 0, 20, 20, v2);
ExpectViewBoundsEquals(40, 0, 20, 20, v3);
// If the limit is zero, behaves as though the columns are not linked.
c1->set_linked_column_size_limit(0);
pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(10 + 20 + 0, 20), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 10, 20, v1);
ExpectViewBoundsEquals(10, 0, 20, 20, v2);
ExpectViewBoundsEquals(30, 0, 0, 20, v3);
// Set a size limit.
c1->set_linked_column_size_limit(40);
v1->SetPreferredSize(gfx::Size(35, 20));
// |v1| now dominates, but it is still below the limit.
pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(35 + 35 + 35, 20), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 35, 20, v1);
ExpectViewBoundsEquals(35, 0, 35, 20, v2);
ExpectViewBoundsEquals(70, 0, 35, 20, v3);
// Go over the limit. |v1| shouldn't influence size at all, but the others
// should still be linked to the next largest width.
v1->SetPreferredSize(gfx::Size(45, 20));
pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(45 + 20 + 20, 20), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 45, 20, v1);
ExpectViewBoundsEquals(45, 0, 20, 20, v2);
ExpectViewBoundsEquals(65, 0, 20, 20, v3);
}
TEST_F(GridLayoutTest, ColSpan1) {
ColumnSet* c1 = layout()->AddColumnSet(0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
1, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1);
layout()->StartRow(0, 0);
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 40)));
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(100, 60), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 100, 20, v1);
ExpectViewBoundsEquals(0, 20, 10, 40, v2);
}
TEST_F(GridLayoutTest, ColSpan2) {
ColumnSet* c1 = layout()->AddColumnSet(0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
1, GridLayout::USE_PREF, 0, 0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1);
layout()->StartRow(0, 0);
layout()->SkipColumns(1);
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(100, 40), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 100, 20, v1);
ExpectViewBoundsEquals(90, 20, 10, 20, v2);
}
TEST_F(GridLayoutTest, ColSpan3) {
ColumnSet* c1 = layout()->AddColumnSet(0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(100, 20)), 2, 1);
layout()->StartRow(0, 0);
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(100, 40), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 100, 20, v1);
ExpectViewBoundsEquals(0, 20, 10, 20, v2);
ExpectViewBoundsEquals(50, 20, 10, 20, v3);
}
TEST_F(GridLayoutTest, ColSpan4) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
GridLayout::USE_PREF, 0, 0);
set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
layout()->StartRow(0, 0);
auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(25, 20)), 2, 1);
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(25, 30), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 10, 10, v1);
ExpectViewBoundsEquals(12, 0, 10, 10, v2);
ExpectViewBoundsEquals(0, 10, 25, 20, v3);
}
// Verifies the sizing of a view that doesn't start in the first column
// and has a column span > 1 (crbug.com/254092).
TEST_F(GridLayoutTest, ColSpanStartSecondColumn) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
GridLayout::USE_PREF, 0, 0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
GridLayout::USE_PREF, 0, 0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
GridLayout::FIXED, 10, 0);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(20, 10)), 2, 1);
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(30, 10), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 10, 10, v1);
ExpectViewBoundsEquals(10, 0, 20, 10, v2);
}
TEST_F(GridLayoutTest, SameSizeColumns) {
ColumnSet* c1 = layout()->AddColumnSet(0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
c1->LinkColumnSizes(0, 1, -1);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20)));
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(100, 20), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 50, 20, v1);
ExpectViewBoundsEquals(50, 0, 10, 10, v2);
}
TEST_F(GridLayoutTest, HorizontalResizeTest1) {
ColumnSet* c1 = layout()->AddColumnSet(0);
c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
1, GridLayout::USE_PREF, 0, 0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20)));
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
host()->SetBounds(0, 0, 110, 20);
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 100, 20, v1);
ExpectViewBoundsEquals(100, 0, 10, 10, v2);
}
TEST_F(GridLayoutTest, HorizontalResizeTest2) {
ColumnSet* c1 = layout()->AddColumnSet(0);
c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
1, GridLayout::USE_PREF, 0, 0);
c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING,
1, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20)));
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
host()->SetBounds(0, 0, 120, 20);
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 80, 20, v1);
ExpectViewBoundsEquals(110, 0, 10, 10, v2);
}
// Tests that space leftover due to rounding is distributed to the last
// resizable column.
TEST_F(GridLayoutTest, HorizontalResizeTest3) {
ColumnSet* c1 = layout()->AddColumnSet(0);
c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
1, GridLayout::USE_PREF, 0, 0);
c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
1, GridLayout::USE_PREF, 0, 0);
c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
auto* v3 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
host()->SetBounds(0, 0, 31, 10);
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 10, 10, v1);
ExpectViewBoundsEquals(10, 0, 11, 10, v2);
ExpectViewBoundsEquals(21, 0, 10, 10, v3);
}
TEST_F(GridLayoutTest, TestVerticalResize1) {
ColumnSet* c1 = layout()->AddColumnSet(0);
c1->AddColumn(GridLayout::FILL, GridLayout::FILL,
1, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(1, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(50, 20)));
layout()->StartRow(0, 0);
auto* v2 = layout()->AddView(CreateSizedView(gfx::Size(10, 10)));
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(50, 30), pref);
host()->SetBounds(0, 0, 50, 100);
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 50, 90, v1);
ExpectViewBoundsEquals(0, 90, 50, 10, v2);
}
TEST_F(GridLayoutTest, Border) {
host()->SetBorder(CreateEmptyBorder(1, 2, 3, 4));
ColumnSet* c1 = layout()->AddColumnSet(0);
c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* v1 = layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(16, 24), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(2, 1, 10, 20, v1);
}
TEST_F(GridLayoutTest, FixedSize) {
host()->SetBorder(CreateEmptyBorder(2, 2, 2, 2));
ColumnSet* set = layout()->AddColumnSet(0);
constexpr size_t kRowCount = 2;
constexpr size_t kColumnCount = 4;
constexpr int kTitleWidth = 100;
constexpr int kPrefWidth = 10;
constexpr int kPrefHeight = 20;
for (size_t i = 0; i < kColumnCount; ++i) {
set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::FIXED,
kTitleWidth, kTitleWidth);
}
for (size_t row = 0; row < kRowCount; ++row) {
layout()->StartRow(0, 0);
for (size_t column = 0; column < kColumnCount; ++column)
layout()->AddView(CreateSizedView(gfx::Size(kPrefWidth, kPrefHeight)));
}
layout()->Layout(host());
auto i = host()->children().cbegin();
for (size_t row = 0; row < kRowCount; ++row) {
for (size_t column = 0; column < kColumnCount; ++column, ++i) {
ExpectViewBoundsEquals(
2 + kTitleWidth * column + (kTitleWidth - kPrefWidth) / 2,
2 + kPrefHeight * row, kPrefWidth, kPrefHeight, *i);
}
}
EXPECT_EQ(
gfx::Size(kColumnCount * kTitleWidth + 4, kRowCount * kPrefHeight + 4),
GetPreferredSize());
}
TEST_F(GridLayoutTest, RowSpanWithPaddingRow) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::CENTER,
GridLayout::CENTER,
0,
GridLayout::FIXED,
10,
10);
layout()->StartRow(0, 0);
layout()->AddView(CreateSizedView(gfx::Size(10, 10)), 1, 2);
layout()->AddPaddingRow(0, 10);
}
TEST_F(GridLayoutTest, RowSpan) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::LEADING,
GridLayout::LEADING,
0,
GridLayout::USE_PREF,
0,
0);
set->AddColumn(GridLayout::LEADING,
GridLayout::LEADING,
0,
GridLayout::USE_PREF,
0,
0);
layout()->StartRow(0, 0);
layout()->AddView(CreateSizedView(gfx::Size(20, 10)));
layout()->AddView(CreateSizedView(gfx::Size(20, 40)), 1, 2);
layout()->StartRow(1, 0);
auto* s3 = layout()->AddView(CreateSizedView(gfx::Size(20, 10)));
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(40, 40), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 10, 20, 10, s3);
}
TEST_F(GridLayoutTest, RowSpan2) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0,GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
layout()->AddView(CreateSizedView(gfx::Size(20, 20)));
auto* s3 = layout()->AddView(CreateSizedView(gfx::Size(64, 64)), 1, 3);
layout()->AddPaddingRow(0, 10);
layout()->StartRow(0, 0);
layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(84, 64), pref);
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(20, 0, 64, 64, s3);
}
TEST_F(GridLayoutTest, FixedViewWidth) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0,GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* view =
layout()->AddView(CreateSizedView(gfx::Size(30, 40)), 1, 1,
GridLayout::LEADING, GridLayout::LEADING, 10, 0);
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(10, pref.width());
EXPECT_EQ(40, pref.height());
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 10, 40, view);
}
TEST_F(GridLayoutTest, FixedViewHeight) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0, GridLayout::USE_PREF, 0, 0);
set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
0,GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
auto* view =
layout()->AddView(CreateSizedView(gfx::Size(30, 40)), 1, 1,
GridLayout::LEADING, GridLayout::LEADING, 0, 10);
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(30, pref.width());
EXPECT_EQ(10, pref.height());
host()->SetBounds(0, 0, pref.width(), pref.height());
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 30, 10, view);
}
// Make sure that for views that span columns the underlying columns are resized
// based on the resize percent of the column.
TEST_F(GridLayoutTest, ColumnSpanResizing) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
2, GridLayout::USE_PREF, 0, 0);
set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
4, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
// span_view spans two columns and is twice as big the views added below.
View* span_view = layout()->AddView(CreateSizedView(gfx::Size(12, 40)), 2, 1,
GridLayout::LEADING, GridLayout::LEADING);
layout()->StartRow(0, 0);
auto* view1 = layout()->AddView(CreateSizedView(gfx::Size(2, 40)));
auto* view2 = layout()->AddView(CreateSizedView(gfx::Size(4, 40)));
host()->SetBounds(0, 0, 12, 80);
layout()->Layout(host());
ExpectViewBoundsEquals(0, 0, 12, 40, span_view);
// view1 should be 4 pixels wide
// column_pref + (remaining_width * column_resize / total_column_resize) =
// 2 + (6 * 2 / 6).
ExpectViewBoundsEquals(0, 40, 4, 40, view1);
// And view2 should be 8 pixels wide:
// 4 + (6 * 4 / 6).
ExpectViewBoundsEquals(4, 40, 8, 40, view2);
}
// Check that GetPreferredSize() takes resizing of columns into account when
// there is additional space in the case we have column sets of different
// preferred sizes.
TEST_F(GridLayoutTest, ColumnResizingOnGetPreferredSize) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
1, GridLayout::USE_PREF, 0, 0);
set = layout()->AddColumnSet(1);
set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
1, GridLayout::USE_PREF, 0, 0);
set = layout()->AddColumnSet(2);
set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
1, GridLayout::USE_PREF, 0, 0);
// Make a row containing a flexible view that trades width for height.
layout()->StartRow(0, 0);
layout()->AddView(std::make_unique<FlexibleView>(100), 1, 1, GridLayout::FILL,
GridLayout::LEADING);
// The second row contains a view of fixed size that will enforce a column
// width of 20 pixels.
layout()->StartRow(0, 1);
layout()->AddView(CreateSizedView(gfx::Size(20, 20)), 1, 1, GridLayout::FILL,
GridLayout::LEADING);
// Add another flexible view in row three in order to ensure column set
// ordering doesn't influence sizing behaviour.
layout()->StartRow(0, 2);
layout()->AddView(std::make_unique<FlexibleView>(40), 1, 1, GridLayout::FILL,
GridLayout::LEADING);
// We expect a height of 50: 30 from the variable width view in the first row
// plus 20 from the statically sized view in the second row. The flexible
// view in the third row should contribute no height.
EXPECT_EQ(gfx::Size(20, 50), GetPreferredSize());
}
TEST_F(GridLayoutTest, MinimumPreferredSize) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL,
0, GridLayout::USE_PREF, 0, 0);
layout()->StartRow(0, 0);
layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
gfx::Size pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(10, 20), pref);
layout()->set_minimum_size(gfx::Size(40, 40));
pref = GetPreferredSize();
EXPECT_EQ(gfx::Size(40, 40), pref);
}
// Test that attempting a Layout() while nested in AddView() causes a DCHECK.
// GridLayout must guard against this as it hasn't yet updated the internal
// structures it uses to calculate Layout, so will give bogus results.
TEST_F(GridLayoutTest, LayoutOnAddDeath) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0,
0);
layout()->StartRow(0, 0);
auto view = std::make_unique<LayoutOnAddView>();
EXPECT_DCHECK_DEATH(layout()->AddView(std::move(view)));
// Death tests use fork(), so nothing should be added here.
EXPECT_FALSE(view->parent());
// If the View has nothing to change, adding should succeed.
view->set_target_size(view->GetPreferredSize());
auto* view_ptr = layout()->AddView(std::move(view));
EXPECT_TRUE(view_ptr->parent());
}
TEST_F(GridLayoutTest, ColumnMinForcesPreferredWidth) {
// Column's min width is greater than views preferred/min width. This should
// force the preferred width to the min width of the column.
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0,
100);
layout()->StartRow(0, 0);
layout()->AddView(CreateSizedView(gfx::Size(20, 10)));
EXPECT_EQ(gfx::Size(100, 10), GetPreferredSize());
}
TEST_F(GridLayoutTest, HonorsColumnMin) {
layout()->set_honors_min_width(true);
// Verifies that a column with a min width is never shrunk smaller than the
// minw width.
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0,
100);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0,
0);
layout()->StartRow(0, 0);
View* view1 = layout()->AddView(
CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(125, 10)));
View* view2 = layout()->AddView(
CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(50, 10)));
EXPECT_EQ(gfx::Size(175, 10), GetPreferredSize());
host()->SetBounds(0, 0, 175, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 125, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(125, 0, 50, 10), view2->bounds());
host()->SetBounds(0, 0, 125, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 100, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(100, 0, 25, 10), view2->bounds());
host()->SetBounds(0, 0, 120, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 100, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(100, 0, 20, 10), view2->bounds());
}
TEST_F(GridLayoutTest, TwoViewsOneSizeSmallerThanMinimum) {
layout()->set_honors_min_width(true);
// Two columns, equally resizable with two views. Only the first view is
// resizable.
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0,
0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 5, GridLayout::USE_PREF, 0,
0);
layout()->StartRow(0, 0);
View* view1 = layout()->AddView(
CreateViewWithMinAndPref(gfx::Size(20, 10), gfx::Size(100, 10)));
View* view2 = layout()->AddView(
CreateViewWithMinAndPref(gfx::Size(100, 10), gfx::Size(100, 10)));
host()->SetBounds(0, 0, 110, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 20, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(20, 0, 100, 10), view2->bounds());
}
TEST_F(GridLayoutTest, TwoViewsBothSmallerThanMinimumDifferentResizeWeights) {
layout()->set_honors_min_width(true);
// Two columns, equally resizable with two views. Only the first view is
// resizable.
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 8, GridLayout::USE_PREF, 0,
0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 2, GridLayout::USE_PREF, 0,
0);
layout()->StartRow(0, 0);
View* view1 = layout()->AddView(
CreateViewWithMinAndPref(gfx::Size(91, 10), gfx::Size(100, 10)));
View* view2 = layout()->AddView(
CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)));
// 200 is the preferred, each should get their preferred width.
host()->SetBounds(0, 0, 200, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 100, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(100, 0, 100, 10), view2->bounds());
// 1 pixel smaller than pref.
host()->SetBounds(0, 0, 199, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 99, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(99, 0, 100, 10), view2->bounds());
// 10 pixels smaller than pref.
host()->SetBounds(0, 0, 190, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 92, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(92, 0, 98, 10), view2->bounds());
// 11 pixels smaller than pref.
host()->SetBounds(0, 0, 189, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 91, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(91, 0, 98, 10), view2->bounds());
// 12 pixels smaller than pref.
host()->SetBounds(0, 0, 188, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 91, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(91, 0, 97, 10), view2->bounds());
host()->SetBounds(0, 0, 5, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 91, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(91, 0, 10, 10), view2->bounds());
}
TEST_F(GridLayoutTest, TwoViewsOneColumnUsePrefOtherFixed) {
layout()->set_honors_min_width(true);
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 8, GridLayout::USE_PREF, 0,
0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 2, GridLayout::FIXED, 100,
0);
layout()->StartRow(0, 0);
View* view1 = layout()->AddView(
CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)));
View* view2 = layout()->AddView(
CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)));
host()->SetBounds(0, 0, 120, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 20, 10), view1->bounds());
// Even though column 2 has a resize percent, it's FIXED, so it won't shrink.
EXPECT_EQ(gfx::Rect(20, 0, 100, 10), view2->bounds());
host()->SetBounds(0, 0, 10, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 10, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(10, 0, 100, 10), view2->bounds());
}
TEST_F(GridLayoutTest, TwoViewsBothColumnsResizableOneViewFixedWidthMin) {
layout()->set_honors_min_width(true);
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0,
0);
set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0,
0);
layout()->StartRow(0, 0);
View* view1 = layout()->AddView(
CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)));
View* view2 = layout()->AddView(
CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10)), 1, 1,
GridLayout::FILL, GridLayout::FILL, 50, 10);
host()->SetBounds(0, 0, 80, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 30, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(30, 0, 50, 10), view2->bounds());
host()->SetBounds(0, 0, 70, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 20, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(20, 0, 50, 10), view2->bounds());
host()->SetBounds(0, 0, 10, 0);
layout()->Layout(host());
EXPECT_EQ(gfx::Rect(0, 0, 10, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(10, 0, 50, 10), view2->bounds());
}
class SettablePreferredHeightView : public View {
public:
explicit SettablePreferredHeightView(int height) : pref_height_(height) {}
~SettablePreferredHeightView() override = default;
// View:
int GetHeightForWidth(int width) const override { return pref_height_; }
private:
const int pref_height_;
DISALLOW_COPY_AND_ASSIGN(SettablePreferredHeightView);
};
TEST_F(GridLayoutTest, HeightForWidthCalledWhenNotGivenPreferredWidth) {
ColumnSet* set = layout()->AddColumnSet(0);
set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, GridLayout::USE_PREF,
0, 0);
layout()->StartRow(0, 0);
const int pref_height = 100;
auto view = std::make_unique<SettablePreferredHeightView>(pref_height);
const gfx::Size pref(10, 20);
view->SetPreferredSize(pref);
layout()->AddView(std::move(view));
EXPECT_EQ(pref, GetPreferredSize());
EXPECT_EQ(pref_height, host()->GetHeightForWidth(5));
}
} // namespace views