| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/views/layout/table_layout.h" |
| |
| #include "base/memory/raw_ptr.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/metadata/metadata_header_macros.h" |
| #include "ui/base/metadata/metadata_impl_macros.h" |
| #include "ui/views/border.h" |
| #include "ui/views/view.h" |
| #include "ui/views/view_class_properties.h" |
| |
| namespace views { |
| |
| namespace { |
| |
| 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 { |
| METADATA_HEADER(MinSizeView, View) |
| |
| public: |
| explicit MinSizeView(const gfx::Size& min_size) : min_size_(min_size) {} |
| |
| MinSizeView(const MinSizeView&) = delete; |
| MinSizeView& operator=(const MinSizeView&) = delete; |
| |
| ~MinSizeView() override = default; |
| |
| // View: |
| gfx::Size GetMinimumSize() const override { return min_size_; } |
| |
| private: |
| const gfx::Size min_size_; |
| }; |
| |
| 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; |
| } |
| |
| BEGIN_METADATA(MinSizeView) |
| END_METADATA |
| |
| } // namespace |
| |
| class TableLayoutTest : public testing::Test { |
| public: |
| TableLayoutTest() : host_(std::make_unique<View>()) { |
| layout_ = host_->SetLayoutManager(std::make_unique<views::TableLayout>()); |
| } |
| |
| gfx::Size GetPreferredSize() { |
| return layout_->GetPreferredSize(host_.get()); |
| } |
| |
| View* host() { return host_.get(); } |
| TableLayout& layout() { return *layout_; } |
| |
| private: |
| std::unique_ptr<View> host_; |
| raw_ptr<TableLayout> layout_; |
| }; |
| |
| class TableLayoutAlignmentTest : public testing::Test { |
| public: |
| TableLayoutAlignmentTest() : host_(std::make_unique<View>()) { |
| layout_ = host_->SetLayoutManager(std::make_unique<views::TableLayout>()); |
| } |
| |
| void TestAlignment(LayoutAlignment alignment, gfx::Rect* bounds) { |
| layout_ |
| ->AddColumn(alignment, alignment, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, 1.0f); |
| auto* v1 = host_->AddChildView(std::make_unique<View>()); |
| v1->SetPreferredSize(gfx::Size(10, 20)); |
| 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->bounds(); |
| } |
| |
| private: |
| std::unique_ptr<View> host_; |
| raw_ptr<TableLayout> layout_; |
| }; |
| |
| TEST_F(TableLayoutAlignmentTest, Fill) { |
| gfx::Rect bounds; |
| TestAlignment(LayoutAlignment::kStretch, &bounds); |
| EXPECT_EQ(gfx::Rect(0, 0, 100, 100), bounds); |
| } |
| |
| TEST_F(TableLayoutAlignmentTest, Leading) { |
| gfx::Rect bounds; |
| TestAlignment(LayoutAlignment::kStart, &bounds); |
| EXPECT_EQ(gfx::Rect(0, 0, 10, 20), bounds); |
| } |
| |
| TEST_F(TableLayoutAlignmentTest, Center) { |
| gfx::Rect bounds; |
| TestAlignment(LayoutAlignment::kCenter, &bounds); |
| EXPECT_EQ(gfx::Rect(45, 40, 10, 20), bounds); |
| } |
| |
| TEST_F(TableLayoutAlignmentTest, Trailing) { |
| gfx::Rect bounds; |
| TestAlignment(LayoutAlignment::kEnd, &bounds); |
| EXPECT_EQ(gfx::Rect(90, 80, 10, 20), bounds); |
| } |
| |
| TEST_F(TableLayoutTest, TwoColumns) { |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(10, 20))); |
| auto* v2 = host()->AddChildView(CreateSizedView(gfx::Size(20, 20))); |
| |
| 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); |
| ExpectViewBoundsEquals(10, 0, 20, 20, v2); |
| } |
| |
| // Test linked column sizes, and the column size limit. |
| TEST_F(TableLayoutTest, LinkedSizes) { |
| // Fill widths. |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .LinkColumnSizes({0, 1, 2}) |
| .AddRows(1, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(10, 20))); |
| auto* v2 = host()->AddChildView(CreateSizedView(gfx::Size(20, 20))); |
| auto* v3 = host()->AddChildView(CreateSizedView(gfx::Size(0, 20))); |
| |
| 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. |
| layout().SetLinkedColumnSizeLimit(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. |
| layout().SetLinkedColumnSizeLimit(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(TableLayoutTest, ColSpan1) { |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(2, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(100, 20))); |
| v1->SetProperty(kTableColAndRowSpanKey, gfx::Size(2, 1)); |
| auto* v2 = host()->AddChildView(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(TableLayoutTest, ColSpan2) { |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(2, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(100, 20))); |
| v1->SetProperty(kTableColAndRowSpanKey, gfx::Size(2, 1)); |
| host()->AddChildView(std::make_unique<View>()); |
| auto* v2 = host()->AddChildView(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(TableLayoutTest, ColSpan3) { |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(2, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(100, 20))); |
| v1->SetProperty(kTableColAndRowSpanKey, gfx::Size(2, 1)); |
| auto* v2 = host()->AddChildView(CreateSizedView(gfx::Size(10, 20))); |
| auto* v3 = host()->AddChildView(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(TableLayoutTest, ColSpan4) { |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(2, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(10, 10))); |
| auto* v2 = host()->AddChildView(CreateSizedView(gfx::Size(10, 10))); |
| auto* v3 = host()->AddChildView(CreateSizedView(gfx::Size(25, 20))); |
| v3->SetProperty(kTableColAndRowSpanKey, gfx::Size(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(TableLayoutTest, ColSpanStartSecondColumn) { |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, |
| TableLayout::kFixedSize, TableLayout::ColumnSize::kFixed, 10, |
| 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(10, 10))); |
| auto* v2 = host()->AddChildView(CreateSizedView(gfx::Size(20, 10))); |
| v2->SetProperty(kTableColAndRowSpanKey, gfx::Size(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(TableLayoutTest, SameSizeColumns) { |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .LinkColumnSizes({0, 1}) |
| .AddRows(1, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(50, 20))); |
| auto* v2 = host()->AddChildView(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(TableLayoutTest, HorizontalResizeTest1) { |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStart, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(50, 20))); |
| auto* v2 = host()->AddChildView(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(TableLayoutTest, HorizontalResizeTest2) { |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStart, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kEnd, LayoutAlignment::kStart, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(50, 20))); |
| auto* v2 = host()->AddChildView(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(TableLayoutTest, HorizontalResizeTest3) { |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStart, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStart, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kEnd, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(10, 10))); |
| auto* v2 = host()->AddChildView(CreateSizedView(gfx::Size(10, 10))); |
| auto* v3 = host()->AddChildView(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(TableLayoutTest, TestVerticalResize1) { |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, 1.0f) |
| .AddRows(1, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(50, 20))); |
| auto* v2 = host()->AddChildView(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(TableLayoutTest, Border) { |
| host()->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(1, 2, 3, 4))); |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(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(TableLayoutTest, FixedSize) { |
| host()->SetBorder(CreateEmptyBorder(2)); |
| |
| 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) { |
| layout().AddColumn(LayoutAlignment::kCenter, LayoutAlignment::kCenter, |
| TableLayout::kFixedSize, TableLayout::ColumnSize::kFixed, |
| kTitleWidth, kTitleWidth); |
| } |
| layout().AddRows(kRowCount, TableLayout::kFixedSize); |
| |
| for (size_t row = 0; row < kRowCount; ++row) { |
| for (size_t column = 0; column < kColumnCount; ++column) { |
| host()->AddChildView(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(TableLayoutTest, RowSpanWithPaddingRow) { |
| layout() |
| .AddColumn(LayoutAlignment::kCenter, LayoutAlignment::kCenter, |
| TableLayout::kFixedSize, TableLayout::ColumnSize::kFixed, 10, |
| 10) |
| .AddRows(1, TableLayout::kFixedSize) |
| .AddPaddingRow(TableLayout::kFixedSize, 10); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(10, 10))); |
| v1->SetProperty(kTableColAndRowSpanKey, gfx::Size(1, 2)); |
| |
| gfx::Size pref = GetPreferredSize(); |
| EXPECT_EQ(gfx::Size(10, 10), pref); |
| |
| host()->SetBounds(0, 0, 10, 20); |
| layout().Layout(host()); |
| ExpectViewBoundsEquals(0, 0, 10, 10, v1); |
| } |
| |
| TEST_F(TableLayoutTest, RowSpan) { |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize) |
| .AddRows(1, 1.0f); |
| host()->AddChildView(CreateSizedView(gfx::Size(20, 10))); |
| auto* v2 = host()->AddChildView(CreateSizedView(gfx::Size(20, 40))); |
| v2->SetProperty(kTableColAndRowSpanKey, gfx::Size(1, 2)); |
| auto* v3 = host()->AddChildView(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, v3); |
| } |
| |
| TEST_F(TableLayoutTest, RowSpan2) { |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize) |
| .AddPaddingRow(TableLayout::kFixedSize, 10) |
| .AddRows(1, TableLayout::kFixedSize); |
| host()->AddChildView(CreateSizedView(gfx::Size(20, 20))); |
| auto* v2 = host()->AddChildView(CreateSizedView(gfx::Size(64, 64))); |
| v2->SetProperty(kTableColAndRowSpanKey, gfx::Size(1, 3)); |
| host()->AddChildView(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, v2); |
| } |
| |
| // Make sure that for views that span columns the underlying columns are resized |
| // based on the resize percent of the column. |
| TEST_F(TableLayoutTest, ColumnSpanResizing) { |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kCenter, 2.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kCenter, 4.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(2, TableLayout::kFixedSize); |
| // span_view spans two columns and is twice as big the views added below. |
| View* span_view = host()->AddChildView(CreateSizedView(gfx::Size(12, 40))); |
| span_view->SetProperty(kTableColAndRowSpanKey, gfx::Size(2, 1)); |
| span_view->SetProperty(kTableHorizAlignKey, LayoutAlignment::kStart); |
| span_view->SetProperty(kTableVertAlignKey, LayoutAlignment::kStart); |
| |
| auto* view1 = host()->AddChildView(CreateSizedView(gfx::Size(2, 40))); |
| auto* view2 = host()->AddChildView(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); |
| } |
| |
| // Make sure that for views that span both fixed and resizable columns the |
| // underlying resiable column is resized and the fixed sized column is not. |
| TEST_F(TableLayoutTest, ColumnSpanResizing2) { |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kCenter, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kCenter, |
| TableLayout::kFixedSize, TableLayout::ColumnSize::kFixed, 10, |
| 0) |
| .AddRows(2, TableLayout::kFixedSize); |
| View* span_view = host()->AddChildView( |
| CreateViewWithMinAndPref(gfx::Size(20, 40), gfx::Size(80, 40))); |
| span_view->SetProperty(kTableColAndRowSpanKey, gfx::Size(2, 1)); |
| |
| auto* view1 = host()->AddChildView(CreateSizedView(gfx::Size(1, 40))); |
| auto* view2 = host()->AddChildView(CreateSizedView(gfx::Size(1, 40))); |
| |
| // Host width is shorter than the preferred width. |
| host()->SetBounds(0, 0, 30, 80); |
| layout().Layout(host()); |
| |
| // `span_view` should shrink to respect the host bound. |
| ExpectViewBoundsEquals(0, 0, 30, 40, span_view); |
| |
| // The first column is host_width - col2_width = 30 - 10 = 20 pixels wide. |
| ExpectViewBoundsEquals(0, 40, 20, 40, view1); |
| |
| // The second column is fixed 10 pixels wide. |
| ExpectViewBoundsEquals(20, 40, 10, 40, view2); |
| } |
| |
| // Make sure that for views that span both fixed and resizable columns the |
| // underlying resizable column is resized and the fixed sized column is not. |
| // The host width in this test is shorter than the minimum size of columns. |
| TEST_F(TableLayoutTest, ColumnSpanResizing3) { |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kCenter, 1.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kCenter, |
| TableLayout::kFixedSize, TableLayout::ColumnSize::kFixed, 10, |
| 0) |
| .AddRows(2, TableLayout::kFixedSize); |
| View* span_view = host()->AddChildView( |
| CreateViewWithMinAndPref(gfx::Size(0, 40), gfx::Size(80, 40))); |
| span_view->SetProperty(kTableColAndRowSpanKey, gfx::Size(2, 1)); |
| |
| auto* view1 = host()->AddChildView( |
| CreateViewWithMinAndPref(gfx::Size(0, 40), gfx::Size(1, 40))); |
| auto* view2 = host()->AddChildView( |
| CreateViewWithMinAndPref(gfx::Size(0, 40), gfx::Size(1, 40))); |
| |
| // Host width is shorter than the minimum size of columns |
| // i.e 5 < col1_min_width + col2_min_width = 0 + 10 = 10. |
| host()->SetBounds(0, 0, 5, 80); |
| layout().Layout(host()); |
| |
| // `span_view` should shrink to the fixed width of col2. |
| ExpectViewBoundsEquals(0, 0, 10, 40, span_view); |
| |
| // The first column is 0 pixels wide. |
| ExpectViewBoundsEquals(0, 40, 0, 40, view1); |
| |
| // The second width column is fixed 10 pixels wide. |
| ExpectViewBoundsEquals(0, 40, 10, 40, view2); |
| } |
| |
| TEST_F(TableLayoutTest, MinimumPreferredSize) { |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| host()->AddChildView(CreateSizedView(gfx::Size(10, 20))); |
| |
| gfx::Size pref = GetPreferredSize(); |
| EXPECT_EQ(gfx::Size(10, 20), pref); |
| |
| layout().SetMinimumSize(gfx::Size(40, 40)); |
| pref = GetPreferredSize(); |
| EXPECT_EQ(gfx::Size(40, 40), pref); |
| } |
| |
| TEST_F(TableLayoutTest, 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. |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 5.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 100) |
| .AddRows(1, TableLayout::kFixedSize); |
| host()->AddChildView(CreateSizedView(gfx::Size(20, 10))); |
| |
| EXPECT_EQ(gfx::Size(100, 10), GetPreferredSize()); |
| } |
| |
| TEST_F(TableLayoutTest, HonorsColumnMin) { |
| // Verifies that a column with a min width is never shrunk smaller than the |
| // min width. |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 5.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 100) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 5.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| View* view1 = host()->AddChildView( |
| CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(125, 10))); |
| View* view2 = host()->AddChildView( |
| 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(TableLayoutTest, TwoViewsOneSizeSmallerThanMinimum) { |
| // Two columns, equally resizable with two views. Only the first view is |
| // resizable. |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 5.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 5.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| View* view1 = host()->AddChildView( |
| CreateViewWithMinAndPref(gfx::Size(20, 10), gfx::Size(100, 10))); |
| View* view2 = host()->AddChildView( |
| 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(TableLayoutTest, TwoViewsBothSmallerThanMinimumDifferentResizeWeights) { |
| // Two columns, equally resizable with two views. Only the first view is |
| // resizable. |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 8.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 2.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| View* view1 = host()->AddChildView( |
| CreateViewWithMinAndPref(gfx::Size(91, 10), gfx::Size(100, 10))); |
| View* view2 = host()->AddChildView( |
| 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(TableLayoutTest, TwoViewsOneColumnUsePrefOtherFixed) { |
| layout() |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 8.0f, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 2.0f, |
| TableLayout::ColumnSize::kFixed, 100, 0) |
| .AddRows(1, TableLayout::kFixedSize); |
| View* view1 = host()->AddChildView( |
| CreateViewWithMinAndPref(gfx::Size(10, 10), gfx::Size(100, 10))); |
| View* view2 = host()->AddChildView( |
| 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(TableLayoutTest, InsufficientChildren) { |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(2, TableLayout::kFixedSize); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(10, 20))); |
| auto* v2 = host()->AddChildView(CreateSizedView(gfx::Size(20, 20))); |
| auto* v3 = host()->AddChildView(CreateSizedView(gfx::Size(10, 20))); |
| |
| gfx::Size pref = GetPreferredSize(); |
| EXPECT_EQ(gfx::Size(30, 40), 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(0, 20, 10, 20, v3); |
| } |
| |
| TEST_F(TableLayoutTest, DistributeRemainingHeight) { |
| layout() |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStart, LayoutAlignment::kStart, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddRows(2, 1.0f); |
| auto* v1 = host()->AddChildView(CreateSizedView(gfx::Size(10, 40))); |
| v1->SetProperty(views::kTableColAndRowSpanKey, gfx::Size(1, 2)); |
| auto* v2 = host()->AddChildView(CreateSizedView(gfx::Size(10, 18))); |
| auto* v3 = host()->AddChildView(CreateSizedView(gfx::Size(10, 19))); |
| |
| // The 3 extra height from v1 (compared to v2 + v3) should be fully |
| // distributed between v2 and v3, so the total height is not less than 40. |
| constexpr gfx::Size kDesiredSize(20, 40); |
| EXPECT_EQ(kDesiredSize, GetPreferredSize()); |
| |
| host()->SetBoundsRect(gfx::Rect(kDesiredSize)); |
| layout().Layout(host()); |
| ExpectViewBoundsEquals(0, 0, 10, 40, v1); |
| ExpectViewBoundsEquals(10, 0, 10, 18, v2); |
| // Because 3 extra height doesn't divide evenly, it gets rounded, so v2 gets |
| // an extra dip compared to v3; thus v3 should start at y = 18 + 2 = 20. |
| ExpectViewBoundsEquals(10, 20, 10, 19, v3); |
| } |
| |
| } // namespace views |