blob: 9f20141e0bb53fb2b43dd0f698a1369460bcc05d [file] [log] [blame]
// Copyright 2018 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/flex_layout.h"
#include <stddef.h>
#include <algorithm>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/safe_conversions.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/gfx/geometry/insets.h"
#include "ui/views/border.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/test/test_views.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"
namespace views {
namespace {
using gfx::Insets;
using gfx::Point;
using gfx::Rect;
using gfx::Size;
using std::optional;
class MockView : public View {
METADATA_HEADER(MockView, View)
public:
enum class SizeMode { kUsePreferredSize, kFixedArea };
void set_preferred_size(gfx::Size preferred_size) {
preferred_size_ = preferred_size;
}
void SetMinimumSize(const Size& minimum_size) {
minimum_size_ = minimum_size;
}
Size GetMinimumSize() const override {
return minimum_size_.value_or(GetPreferredSize({}));
}
void SetMaximumSize(gfx::Size maximum_size) { maximum_size_ = maximum_size; }
Size GetMaximumSize() const override { return maximum_size_; }
gfx::Size CalculatePreferredSize(
const SizeBounds& available_size) const override {
gfx::Size preferred_size =
preferred_size_ ? preferred_size_.value()
: View::CalculatePreferredSize(available_size);
switch (size_mode_) {
case SizeMode::kUsePreferredSize:
return preferred_size;
case SizeMode::kFixedArea: {
int width = available_size.width().min_of(preferred_size.width());
if (width <= 0) {
return preferred_size;
}
width = std::max(width, minimum_size_ ? minimum_size_->width() : 0);
return gfx::Size(
width, (preferred_size.width() * preferred_size.height()) / width);
}
}
}
void set_size_mode(SizeMode size_mode) { size_mode_ = size_mode; }
void SetVisible(bool visible) override {
View::SetVisible(visible);
++set_visible_count_;
}
int GetSetVisibleCount() const { return set_visible_count_; }
void ResetCounts() { set_visible_count_ = 0; }
private:
std::optional<gfx::Size> preferred_size_;
optional<Size> minimum_size_;
gfx::Size maximum_size_;
int set_visible_count_ = 0;
SizeMode size_mode_ = SizeMode::kUsePreferredSize;
};
BEGIN_METADATA(MockView)
ADD_PROPERTY_METADATA(gfx::Size, MaximumSize)
END_METADATA
// Custom flex rule that snaps a view between its preferred size and half that
// size in each dimension.
Size CustomFlexImpl(bool snap_to_zero,
const View* view,
const SizeBounds& maximum_size) {
const Size large_size = view->GetPreferredSize({});
const Size small_size = Size(large_size.width() / 2, large_size.height() / 2);
int horizontal = 0;
if (maximum_size.width() >= large_size.width()) {
horizontal = large_size.width();
} else if (maximum_size.width() >= small_size.width() || !snap_to_zero) {
horizontal = small_size.width();
}
int vertical = 0;
if (maximum_size.height() >= large_size.height()) {
vertical = large_size.height();
} else if (maximum_size.height() >= small_size.height() || !snap_to_zero) {
vertical = small_size.height();
}
return Size(horizontal, vertical);
}
class FlexLayoutTest : public testing::Test {
public:
void SetUp() override {
host_ = std::make_unique<View>();
layout_ = host_->SetLayoutManager(std::make_unique<FlexLayout>());
}
MockView* AddChild(const Size& preferred_size,
const optional<Size>& minimum_size = optional<Size>(),
bool visible = true) {
return AddChild(host_.get(), preferred_size, minimum_size, visible);
}
static MockView* AddChild(
View* parent,
const Size& preferred_size,
const optional<Size>& minimum_size = optional<Size>(),
bool visible = true) {
MockView* const child = new MockView();
child->set_preferred_size(preferred_size);
if (minimum_size.has_value()) {
child->SetMinimumSize(minimum_size.value());
}
if (!visible) {
child->SetVisible(false);
}
parent->AddChildViewRaw(child);
return child;
}
std::vector<Rect> GetChildBounds() const {
std::vector<Rect> result;
std::ranges::transform(host_->children(), std::back_inserter(result),
[](const View* v) {
return v->GetVisible() ? v->bounds() : gfx::Rect();
});
return result;
}
protected:
// Constants re-used in many tests.
static constexpr Insets kSmallInsets = Insets::TLBR(1, 2, 3, 4);
static constexpr Insets kLayoutInsets = Insets::TLBR(5, 6, 7, 9);
static constexpr Insets kLargeInsets = Insets::TLBR(10, 11, 12, 13);
static constexpr Size kChild1Size = Size(12, 10);
static constexpr Size kChild2Size = Size(13, 11);
static constexpr Size kChild3Size = Size(17, 13);
// Use preferred size, but adjust height for width.
static const FlexSpecification kPreferredAdjustHeight;
// Preferred size or drop out.
static const FlexSpecification kDropOut;
static const FlexSpecification kDropOutHighPriority;
// Scale from preferred down to minimum or zero.
static const FlexSpecification kFlex1ScaleToMinimum;
static const FlexSpecification kFlex2ScaleToMinimum;
static const FlexSpecification kFlex1ScaleToMinimumHighPriority;
static const FlexSpecification kFlex1ScaleToZero;
// Scale from a minimum value up to infinity.
static const FlexSpecification kUnbounded;
static const FlexSpecification kUnboundedSnapToMinimum;
static const FlexSpecification kUnboundedSnapToZero;
static const FlexSpecification kUnboundedScaleToMinimumSnapToZero;
static const FlexSpecification kUnboundedScaleToZero;
static const FlexSpecification kUnboundedScaleToZeroAdjustHeight;
static const FlexSpecification kUnboundedScaleToMinimum;
static const FlexSpecification kUnboundedScaleToMinimumHighPriority;
// Scale from a minimum value up to infinity, but only on the horizontal axis.
static const FlexSpecification kUnboundedSnapToMinimumHorizontal;
static const FlexSpecification kUnboundedScaleToMinimumSnapToZeroHorizontal;
static const FlexSpecification kUnboundedScaleToZeroHorizontal;
// Scale from a minimum value up to a maximum value.
static const FlexSpecification kScaleToMaximum;
// Custom flex which scales step-wise.
static const FlexSpecification kCustomFlex;
static const FlexSpecification kCustomFlexSnapToZero;
std::unique_ptr<View> host_;
raw_ptr<FlexLayout> layout_;
};
// static
constexpr Insets FlexLayoutTest::kSmallInsets;
constexpr Insets FlexLayoutTest::kLayoutInsets;
constexpr Insets FlexLayoutTest::kLargeInsets;
constexpr Size FlexLayoutTest::kChild1Size;
constexpr Size FlexLayoutTest::kChild2Size;
constexpr Size FlexLayoutTest::kChild3Size;
const FlexSpecification FlexLayoutTest::kPreferredAdjustHeight =
FlexSpecification(MinimumFlexSizeRule::kPreferred,
MaximumFlexSizeRule::kPreferred,
true)
.WithWeight(0);
const FlexSpecification FlexLayoutTest::kDropOut =
FlexSpecification(MinimumFlexSizeRule::kPreferredSnapToZero,
MaximumFlexSizeRule::kPreferred)
.WithWeight(0)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kDropOutHighPriority =
FlexLayoutTest::kDropOut.WithOrder(1);
const FlexSpecification FlexLayoutTest::kFlex1ScaleToZero =
FlexSpecification(MinimumFlexSizeRule::kScaleToZero,
MaximumFlexSizeRule::kPreferred)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kFlex1ScaleToMinimum =
FlexSpecification(MinimumFlexSizeRule::kScaleToMinimum,
MaximumFlexSizeRule::kPreferred)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kFlex2ScaleToMinimum =
FlexLayoutTest::kFlex1ScaleToMinimum.WithWeight(2);
const FlexSpecification FlexLayoutTest::kFlex1ScaleToMinimumHighPriority =
FlexLayoutTest::kFlex1ScaleToMinimum.WithOrder(1);
const FlexSpecification FlexLayoutTest::kUnbounded =
FlexSpecification(MinimumFlexSizeRule::kPreferred,
MaximumFlexSizeRule::kUnbounded)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedSnapToMinimum =
FlexSpecification(MinimumFlexSizeRule::kPreferredSnapToMinimum,
MaximumFlexSizeRule::kUnbounded)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedSnapToZero =
FlexSpecification(MinimumFlexSizeRule::kPreferredSnapToZero,
MaximumFlexSizeRule::kUnbounded)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToMinimumSnapToZero =
FlexSpecification(MinimumFlexSizeRule::kScaleToMinimumSnapToZero,
MaximumFlexSizeRule::kUnbounded)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToZero =
FlexSpecification(MinimumFlexSizeRule::kScaleToZero,
MaximumFlexSizeRule::kUnbounded)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToZeroAdjustHeight =
FlexSpecification(MinimumFlexSizeRule::kScaleToZero,
MaximumFlexSizeRule::kUnbounded,
true)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToMinimumHighPriority(
MinimumFlexSizeRule::kScaleToMinimum,
MaximumFlexSizeRule::kUnbounded);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToMinimum =
kUnboundedScaleToMinimumHighPriority.WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedSnapToMinimumHorizontal =
FlexSpecification(LayoutOrientation::kHorizontal,
MinimumFlexSizeRule::kPreferredSnapToMinimum,
MaximumFlexSizeRule::kUnbounded)
.WithOrder(2);
const FlexSpecification
FlexLayoutTest::kUnboundedScaleToMinimumSnapToZeroHorizontal =
FlexSpecification(LayoutOrientation::kHorizontal,
MinimumFlexSizeRule::kScaleToMinimumSnapToZero,
MaximumFlexSizeRule::kUnbounded)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToZeroHorizontal =
FlexSpecification(LayoutOrientation::kHorizontal,
MinimumFlexSizeRule::kScaleToZero,
MaximumFlexSizeRule::kUnbounded)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kScaleToMaximum =
FlexSpecification(MinimumFlexSizeRule::kPreferred,
MaximumFlexSizeRule::kScaleToMaximum)
.WithOrder(2);
const FlexSpecification FlexLayoutTest::kCustomFlex =
FlexSpecification(base::BindRepeating(&CustomFlexImpl, false)).WithOrder(2);
const FlexSpecification FlexLayoutTest::kCustomFlexSnapToZero =
FlexSpecification(base::BindRepeating(&CustomFlexImpl, true)).WithOrder(2);
} // namespace
// Size Tests ------------------------------------------------------------------
TEST_F(FlexLayoutTest, GetMinimumSize_Empty) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(false);
EXPECT_EQ(Size(0, 0), host_->GetMinimumSize());
}
TEST_F(FlexLayoutTest, GetMinimumSize_Empty_ViewInsets_Horizontal) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(false);
host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
EXPECT_EQ(Size(15, 12), host_->GetMinimumSize());
}
TEST_F(FlexLayoutTest, GetMinimumSize_Empty_ViewInsets_Vertical) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(false);
host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
EXPECT_EQ(Size(15, 12), host_->GetMinimumSize());
}
TEST_F(FlexLayoutTest, GetMinimumSize_Empty_InternalMargin_Collapsed) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
}
TEST_F(FlexLayoutTest, GetMinimumSize_Empty_InternalMargin_NotCollapsed) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(false);
layout_->SetInteriorMargin(kLayoutInsets);
EXPECT_EQ(Size(15, 12), host_->GetMinimumSize());
}
TEST_F(FlexLayoutTest,
GetMinimumSize_Empty_InternalMargin_DefaultMarginHasNoEffect) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(false);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(11));
EXPECT_EQ(Size(15, 12), host_->GetMinimumSize());
}
TEST_F(FlexLayoutTest, GetMinimumSize_MinimumCross_Horizontal) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMinimumCrossAxisSize(5);
EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
layout_->SetMinimumCrossAxisSize(10);
EXPECT_EQ(Size(9, 10), host_->GetMinimumSize());
host_->SetBorder(CreateEmptyBorder(kSmallInsets));
EXPECT_EQ(Size(15, 14), host_->GetMinimumSize());
}
TEST_F(FlexLayoutTest, GetMinimumSize_MinimumCross_Vertical) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMinimumCrossAxisSize(5);
EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
layout_->SetMinimumCrossAxisSize(10);
EXPECT_EQ(Size(10, 7), host_->GetMinimumSize());
host_->SetBorder(CreateEmptyBorder(kSmallInsets));
EXPECT_EQ(Size(16, 11), host_->GetMinimumSize());
}
// Visibility and Inclusion Tests ----------------------------------------------
TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeInstall) {
// Since our test fixture creates a host and adds the layout manager right
// away, we need to create our own for this test.
std::unique_ptr<views::View> host = std::make_unique<views::View>();
View* child1 =
AddChild(host.get(), Size(10, 10), std::optional<Size>(), false);
View* child2 =
AddChild(host.get(), Size(10, 10), std::optional<Size>(), true);
host->SetLayoutManager(std::make_unique<FlexLayout>());
test::RunScheduledLayout(host.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
child1->SetVisible(true);
child2->SetVisible(false);
test::RunScheduledLayout(host.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_FALSE(child2->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterInstall) {
// Unlike the last test, we'll use the built-in host and layout manager since
// they're already set up.
View* child1 = AddChild(Size(10, 10), std::optional<Size>(), false);
View* child2 = AddChild(Size(10, 10), std::optional<Size>(), true);
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
child1->SetVisible(true);
child2->SetVisible(false);
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_FALSE(child2->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size, optional<Size>(), false);
View* child3 = AddChild(kChild3Size);
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
EXPECT_EQ(Size(44, 25), host_->GetPreferredSize({}));
// This should have no additional effect since the child is already invisible.
child2->SetVisible(false);
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
EXPECT_EQ(Size(44, 25), host_->GetPreferredSize({}));
child2->SetVisible(true);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
Rect(31, 5, 17, 13)};
EXPECT_TRUE(child2->GetVisible());
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterAdd) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child2->SetVisible(false);
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
EXPECT_EQ(Size(44, 25), host_->GetPreferredSize({}));
child2->SetVisible(true);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
Rect(31, 5, 17, 13)};
EXPECT_TRUE(child2->GetVisible());
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest,
Layout_ViewVisibilitySetNotContingentOnActualVisibility) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
// Layout makes child view invisible due to flex rule.
host_->SetSize(Size(40, 25));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
// Preferred size should still reflect child hidden due to flex rule.
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
// Now we will make child explicitly hidden.
child2->SetVisible(false);
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
EXPECT_EQ(Size(44, 25), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, Layout_Exlcude) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
const View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
const View* child3 = AddChild(kChild3Size);
child2->SetProperty(kViewIgnoredByLayoutKey, true);
child2->SetBounds(3, 3, 3, 3);
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Rect(3, 3, 3, 3), child2->bounds());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
EXPECT_EQ(Size(44, 25), host_->GetPreferredSize({}));
child2->SetProperty(kViewIgnoredByLayoutKey, false);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
Rect(31, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}
// Child Positioning Tests -----------------------------------------------------
TEST_F(FlexLayoutTest, LayoutSingleView_Horizontal) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
View* child = AddChild(kChild1Size);
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}
TEST_F(FlexLayoutTest, LayoutSingleView_Vertical) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
View* child = AddChild(kChild1Size);
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossStart) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
AddChild(kChild1Size);
AddChild(kChild2Size);
AddChild(kChild3Size);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
Rect(31, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossCenter) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
AddChild(kChild1Size);
AddChild(kChild2Size);
AddChild(kChild3Size);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(6, 6, 12, 10), Rect(18, 6, 13, 11),
Rect(31, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossEnd) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
AddChild(kChild1Size);
AddChild(kChild2Size);
AddChild(kChild3Size);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(6, 8, 12, 10), Rect(18, 7, 13, 11),
Rect(31, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossStretch) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
host_->SetSize(Size(100, 25));
AddChild(kChild1Size);
AddChild(kChild2Size);
AddChild(kChild3Size);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(6, 5, 12, 13), Rect(18, 5, 13, 13),
Rect(31, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossStart) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
AddChild(kChild1Size);
AddChild(kChild2Size);
AddChild(kChild3Size);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(6, 15, 13, 11),
Rect(6, 26, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(32, 46), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossCenter) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
AddChild(kChild1Size);
AddChild(kChild2Size);
AddChild(kChild3Size);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(8, 5, 12, 10), Rect(8, 15, 13, 11),
Rect(6, 26, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(32, 46), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossEnd) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
AddChild(kChild1Size);
AddChild(kChild2Size);
AddChild(kChild3Size);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(11, 5, 12, 10), Rect(10, 15, 13, 11),
Rect(6, 26, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(32, 46), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossStretch) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
AddChild(kChild1Size);
AddChild(kChild2Size);
AddChild(kChild3Size);
host_->SetSize(Size(32, 50));
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(6, 5, 17, 10), Rect(6, 15, 17, 11),
Rect(6, 26, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(32, 46), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest,
LayoutMultipleViews_MarginAndSpacing_NoCollapse_Horizontal) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(false);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
Rect(31, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
host_->InvalidateLayout();
test::RunScheduledLayout(host_.get());
expected = std::vector<Rect>{Rect(27, 25, 12, 10), Rect(62, 5, 13, 11),
Rect(75, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(101, 64), host_->GetPreferredSize({}));
child2->SetProperty(views::kMarginsKey, Insets(1));
host_->InvalidateLayout();
layout_->SetDefault(views::kMarginsKey, gfx::Insets::VH(0, 3));
test::RunScheduledLayout(host_.get());
expected = std::vector<Rect>{Rect(27, 25, 12, 10), Rect(63, 6, 13, 11),
Rect(80, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(109, 64), host_->GetPreferredSize({}));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->InvalidateLayout();
test::RunScheduledLayout(host_.get());
expected = std::vector<Rect>{Rect(27, 25, 12, 10), Rect(63, 6, 13, 11),
Rect(79, 7, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(107, 64), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest,
LayoutMultipleViews_MarginAndSpacing_NoCollapse_Vertical) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(false);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->InvalidateLayout();
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(27, 25, 12, 10), Rect(7, 58, 13, 11),
Rect(8, 72, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(71, 94), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest,
LayoutMultipleViews_MarginAndSpacing_Collapse_Horizontal) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->InvalidateLayout();
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(21, 20, 12, 10), Rect(56, 5, 13, 11),
Rect(71, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(97, 52), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_MarginAndSpacing_Collapse_Vertical) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->InvalidateLayout();
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(21, 20, 12, 10), Rect(6, 52, 13, 11),
Rect(6, 65, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(56, 85), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(10));
View* child = AddChild(Size(13, 15));
AddChild(kChild3Size);
child->SetProperty(views::kInternalPaddingKey, Insets::TLBR(1, 2, 4, 8));
host_->InvalidateLayout();
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {
Rect(8, 9, 13, 15),
Rect(23, 10, 17, 13),
};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(50, 33), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding_Margins) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(2));
View* child = AddChild(Size(13, 15));
View* child2 = AddChild(kChild3Size);
child->SetProperty(views::kInternalPaddingKey, Insets::TLBR(1, 2, 4, 8));
child2->SetProperty(views::kMarginsKey, Insets(5));
host_->InvalidateLayout();
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {
Rect(4, 4, 13, 15),
Rect(17, 5, 17, 13),
};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(43, 25), host_->GetPreferredSize({}));
}
TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding_Additive) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(20));
View* child = AddChild(Size(13, 15));
View* child2 = AddChild(kChild3Size);
child->SetProperty(views::kInternalPaddingKey, Insets::TLBR(1, 2, 4, 8));
child2->SetProperty(views::kInternalPaddingKey, Insets(5));
host_->InvalidateLayout();
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {
Rect(18, 19, 13, 15),
Rect(38, 15, 17, 13),
};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(70, 50), host_->GetPreferredSize({}));
}
// Height-for-width tests ------------------------------------------------------
TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStart) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(kMarginsKey, gfx::Insets(5));
layout_->SetDefault(kFlexBehaviorKey, kPreferredAdjustHeight);
AddChild({10, 10})->set_size_mode(MockView::SizeMode::kFixedArea);
AddChild({10, 10});
EXPECT_EQ(gfx::Size(20, 40), host_->GetPreferredSize({}));
EXPECT_EQ(40, host_->GetHeightForWidth(26));
EXPECT_EQ(40, host_->GetHeightForWidth(20));
EXPECT_EQ(46, host_->GetHeightForWidth(16));
host_->SizeToPreferredSize();
std::vector<gfx::Rect> expected = {{5, 5, 10, 10}, {5, 25, 10, 10}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({26, 50});
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({20, 50});
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({16, 50});
expected = {{5, 5, 6, 16}, {5, 31, 6, 10}};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest,
HeightForWidth_Vertical_CrossStretch_WidthChangesHeight) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
layout_->SetDefault(kMarginsKey, gfx::Insets(5));
layout_->SetDefault(kFlexBehaviorKey, kPreferredAdjustHeight);
AddChild({10, 10})->set_size_mode(MockView::SizeMode::kFixedArea);
AddChild({10, 10});
EXPECT_EQ(gfx::Size(20, 40), host_->GetPreferredSize({}));
EXPECT_EQ(40, host_->GetHeightForWidth(26));
EXPECT_EQ(40, host_->GetHeightForWidth(20));
EXPECT_EQ(46, host_->GetHeightForWidth(16));
host_->SizeToPreferredSize();
std::vector<gfx::Rect> expected = {{5, 5, 10, 10}, {5, 25, 10, 10}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({26, 50});
expected = {{5, 5, 16, 10}, {5, 25, 16, 10}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({20, 50});
expected = {{5, 5, 10, 10}, {5, 25, 10, 10}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({16, 50});
expected = {{5, 5, 6, 16}, {5, 31, 6, 10}};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStretch_FlexPreferredSize) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
layout_->SetDefault(kMarginsKey, gfx::Insets(5));
layout_->SetDefault(kFlexBehaviorKey, kUnboundedScaleToZeroAdjustHeight);
AddChild({10, 10})->set_size_mode(MockView::SizeMode::kFixedArea);
AddChild({10, 10});
EXPECT_EQ(gfx::Size(20, 40), host_->GetPreferredSize({}));
EXPECT_EQ(40, host_->GetHeightForWidth(26));
EXPECT_EQ(40, host_->GetHeightForWidth(20));
EXPECT_EQ(46, host_->GetHeightForWidth(16));
host_->SizeToPreferredSize();
test::RunScheduledLayout(host_.get());
std::vector<gfx::Rect> expected = {{5, 5, 10, 10}, {5, 25, 10, 10}};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStretch_FlexLarger) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
layout_->SetDefault(kMarginsKey, gfx::Insets(5));
layout_->SetDefault(kFlexBehaviorKey, kUnboundedScaleToZeroAdjustHeight);
AddChild({10, 10})->set_size_mode(MockView::SizeMode::kFixedArea);
AddChild({10, 10});
host_->SetSize({26, 50});
test::RunScheduledLayout(host_.get());
std::vector<gfx::Rect> expected = {{5, 5, 16, 15}, {5, 30, 16, 15}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({20, 50});
test::RunScheduledLayout(host_.get());
expected = {{5, 5, 10, 15}, {5, 30, 10, 15}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({16, 50});
test::RunScheduledLayout(host_.get());
expected = {{5, 5, 6, 18}, {5, 33, 6, 12}};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStretch_FlexSmaller) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
layout_->SetDefault(kMarginsKey, gfx::Insets(5));
layout_->SetDefault(kFlexBehaviorKey, kUnboundedScaleToZeroAdjustHeight);
AddChild({10, 10})->set_size_mode(MockView::SizeMode::kFixedArea);
AddChild({10, 10});
EXPECT_EQ(gfx::Size(20, 40), host_->GetPreferredSize({}));
EXPECT_EQ(40, host_->GetHeightForWidth(26));
EXPECT_EQ(40, host_->GetHeightForWidth(20));
EXPECT_EQ(46, host_->GetHeightForWidth(16));
host_->SetSize({26, 30});
test::RunScheduledLayout(host_.get());
std::vector<gfx::Rect> expected = {{5, 5, 16, 5}, {5, 20, 16, 5}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({20, 30});
test::RunScheduledLayout(host_.get());
expected = {{5, 5, 10, 5}, {5, 20, 10, 5}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({16, 30});
test::RunScheduledLayout(host_.get());
expected = {{5, 5, 6, 8}, {5, 23, 6, 2}};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, HeightForWidth_Horizontal_PreferredSize) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(kFlexBehaviorKey, kUnboundedScaleToZeroAdjustHeight);
MockView* const child = AddChild({10, 10});
child->set_size_mode(MockView::SizeMode::kFixedArea);
// In horizontal views, the height can expand if a child is compressed
// horizontally and uses a height-for-width calculation, but it cannot
// contract (lest we have zero-height views in some cases).
EXPECT_EQ(gfx::Size(10, 10), host_->GetPreferredSize({}));
EXPECT_EQ(10, host_->GetHeightForWidth(10));
EXPECT_EQ(10, host_->GetHeightForWidth(20));
EXPECT_EQ(20, host_->GetHeightForWidth(5));
}
// Host insets tests -----------------------------------------------------------
TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
View* child = AddChild(kChild1Size);
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}
TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical) {
layout_->SetOrientation(LayoutOrientation::kVertical);
host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
View* child = AddChild(kChild1Size);
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}
TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_Leading) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
View* child = AddChild(kChild1Size);
host_->SetSize({100, 100});
EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}
TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_Leading) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
View* child = AddChild(kChild1Size);
host_->SetSize({100, 100});
EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}
TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_Center) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetMainAxisAlignment(LayoutAlignment::kCenter);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
View* child = AddChild(kChild1Size);
host_->SetSize({100, 100});
const int expected_x =
kLayoutInsets.left() +
(host_->size().width() - kChild1Size.width() - kLayoutInsets.width()) / 2;
EXPECT_EQ(Rect(expected_x, 5, 12, 10), child->bounds());
}
TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_Center) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kCenter);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
View* child = AddChild(kChild1Size);
host_->SetSize({100, 100});
const int expected_y =
kLayoutInsets.top() +
(host_->size().height() - kChild1Size.height() - kLayoutInsets.height()) /
2;
EXPECT_EQ(Rect(6, expected_y, 12, 10), child->bounds());
}
TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_End) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetMainAxisAlignment(LayoutAlignment::kEnd);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
View* child = AddChild(kChild1Size);
host_->SetSize({100, 100});
const int expected_x =
kLayoutInsets.left() +
(host_->size().width() - kChild1Size.width() - kLayoutInsets.width());
EXPECT_EQ(Rect(expected_x, 5, 12, 10), child->bounds());
}
TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_End) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kEnd);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
View* child = AddChild(kChild1Size);
host_->SetSize({100, 100});
const int expected_y =
kLayoutInsets.top() +
(host_->size().height() - kChild1Size.height() - kLayoutInsets.height());
EXPECT_EQ(Rect(6, expected_y, 12, 10), child->bounds());
}
// Include Host Insets Tests ---------------------------------------------------
TEST_F(FlexLayoutTest, SetIncludeHostInsetsInLayout_NoChange) {
host_->SetBorder(views::CreateEmptyBorder(2));
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(false);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(4));
constexpr Size kChildSize(10, 10);
AddChild(kChildSize);
View* const child2 = AddChild(kChildSize);
AddChild(kChildSize);
child2->SetProperty(views::kMarginsKey, gfx::Insets(10));
const Size expected_preferred_size = host_->GetPreferredSize({});
host_->SetSize(expected_preferred_size);
const std::vector<Rect> expected_bounds = GetChildBounds();
layout_->SetIncludeHostInsetsInLayout(true);
const Size preferred_size = host_->GetPreferredSize({});
EXPECT_EQ(expected_preferred_size, preferred_size);
test::RunScheduledLayout(host_.get());
EXPECT_EQ(expected_bounds, GetChildBounds());
}
TEST_F(FlexLayoutTest, SetIncludeHostInsetsInLayout_CollapseIntoInsets) {
host_->SetBorder(views::CreateEmptyBorder(Insets(2)));
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(4));
constexpr Size kChildSize(10, 10);
AddChild(kChildSize);
View* const child2 = AddChild(kChildSize);
AddChild(kChildSize);
child2->SetProperty(views::kMarginsKey, gfx::Insets(15));
layout_->SetIncludeHostInsetsInLayout(true);
const Size preferred_size = host_->GetPreferredSize({});
EXPECT_EQ(Size(40, 76), preferred_size);
host_->SetSize(preferred_size);
const std::vector<Rect> expected = {Rect(19, 7, 10, 10), Rect(15, 32, 10, 10),
Rect(19, 57, 10, 10)};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, SetIncludeHostInsetsInLayout_OverlapInsets) {
host_->SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(4, 5, 5, 5)));
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
View* const child = AddChild(Size(10, 10));
child->SetProperty(views::kInternalPaddingKey, Insets(10));
layout_->SetIncludeHostInsetsInLayout(true);
const Size preferred_size = host_->GetPreferredSize({});
EXPECT_EQ(Size(15, 12), preferred_size);
host_->SetSize(preferred_size);
EXPECT_EQ(Rect(1, 0, 10, 10), child->bounds());
}
// Default Main Axis Margins Tests ---------------------------------------------
TEST_F(FlexLayoutTest, SetIgnoreDefaultMainAxisMargins_IgnoresDefaultMargins) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(false);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(4));
constexpr Size kChildSize(10, 10);
AddChild(kChildSize);
AddChild(kChildSize);
AddChild(kChildSize);
Size preferred_size = host_->GetPreferredSize({});
EXPECT_EQ(Size(33, 66), preferred_size);
layout_->SetIgnoreDefaultMainAxisMargins(true);
preferred_size = host_->GetPreferredSize({});
EXPECT_EQ(Size(33, 58), preferred_size);
host_->SetSize(preferred_size);
const std::vector<Rect> expected = {Rect(10, 5, 10, 10), Rect(10, 23, 10, 10),
Rect(10, 41, 10, 10)};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest,
SetIgnoreDefaultMainAxisMargins_IncludesExplicitMargins) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(4));
constexpr Size kChildSize(10, 10);
View* const child1 = AddChild(kChildSize);
AddChild(kChildSize);
View* const child3 = AddChild(kChildSize);
child1->SetProperty(views::kMarginsKey, gfx::Insets(11));
child3->SetProperty(views::kMarginsKey, gfx::Insets(12));
Size preferred_size = host_->GetPreferredSize({});
EXPECT_EQ(Size(34, 76), preferred_size);
layout_->SetIgnoreDefaultMainAxisMargins(true);
preferred_size = host_->GetPreferredSize({});
EXPECT_EQ(Size(34, 76), preferred_size);
host_->SetSize(preferred_size);
const std::vector<Rect> expected = {Rect(11, 11, 10, 10), Rect(6, 32, 10, 10),
Rect(12, 54, 10, 10)};
EXPECT_EQ(expected, GetChildBounds());
}
// Alignment Tests -------------------------------------------------------------
TEST_F(FlexLayoutTest, Layout_CrossStart) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->SetSize(Size(200, 200));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(10, child1->origin().y());
EXPECT_EQ(5, child2->origin().y());
EXPECT_EQ(5, child3->origin().y());
}
TEST_F(FlexLayoutTest, Layout_CrossCenter) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->SetSize(Size(200, 200));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(94, child1->origin().y());
EXPECT_EQ(93, child2->origin().y());
EXPECT_EQ(92, child3->origin().y());
}
TEST_F(FlexLayoutTest, Layout_CrossEnd) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->SetSize(Size(200, 200));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(178, child1->origin().y());
EXPECT_EQ(182, child2->origin().y());
EXPECT_EQ(180, child3->origin().y());
}
TEST_F(FlexLayoutTest, Layout_CrossStretch) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->SetSize(Size(200, 200));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(10, child1->origin().y());
EXPECT_EQ(5, child2->origin().y());
EXPECT_EQ(5, child3->origin().y());
EXPECT_EQ(178, child1->size().height());
EXPECT_EQ(188, child2->size().height());
EXPECT_EQ(188, child3->size().height());
}
TEST_F(FlexLayoutTest, Layout_AlignStart) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->SetSize(Size(105, 50));
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(21, 20, 12, 10), Rect(56, 5, 13, 11),
Rect(71, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, Layout_AlignCenter) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kCenter);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->SetSize(Size(105, 50));
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(25, 20, 12, 10), Rect(60, 5, 13, 11),
Rect(75, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, Layout_AlignEnd) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kEnd);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->SetSize(Size(105, 50));
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(29, 20, 12, 10), Rect(64, 5, 13, 11),
Rect(79, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, Layout_AddDroppedMargins) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(false);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
View* child1 = AddChild(Size(10, 10));
View* child2 = AddChild(Size(10, 10));
View* child3 = AddChild(Size(10, 10));
child2->SetProperty(views::kMarginsKey, Insets(1));
child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
EXPECT_EQ(Size(30, 20), host_->GetMinimumSize());
host_->SetSize(Size(100, 50));
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(5, 5, 10, 10), Rect(16, 6, 10, 10),
Rect(27, 5, 10, 10)};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize(Size(25, 50));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Rect(5, 5, 10, 10), child1->bounds());
EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(15, 5, 10, 10), child3->bounds());
}
TEST_F(FlexLayoutTest, Layout_VerticalAlign_WiderThanTall) {
// This test ensures we do not regress http://crbug.com/983941
// Previously, the width of the host view was erroneously used when
// calculating excess main-axis size, causing center-alignment in vertical
// layouts in host views that were much wider than tall to be incorrect.
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kCenter);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->SetSize(Size(1000, 100));
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(21, 27, 12, 10), Rect(6, 59, 13, 11),
Rect(6, 72, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
}
// Flex Tests ------------------------------------------------------------------
TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropViews) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
host_->SetSize(Size(55, 50));
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected = {Rect(11, 10, 12, 10), Rect(36, 5, 13, 11),
Rect(51, 5, 17, 13)};
EXPECT_EQ(expected, GetChildBounds());
child1->SetProperty(views::kFlexBehaviorKey, kDropOut);
host_->InvalidateLayout();
EXPECT_EQ(Size(77, 32), host_->GetPreferredSize({}));
EXPECT_EQ(Size(47, 25), host_->GetMinimumSize());
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_TRUE(child3->GetVisible());
EXPECT_EQ(Rect(6, 5, 13, 11), child2->bounds());
EXPECT_EQ(Rect(21, 5, 17, 13), child3->bounds());
child1->ClearProperty(views::kFlexBehaviorKey);
child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
host_->InvalidateLayout();
EXPECT_EQ(Size(77, 32), host_->GetPreferredSize({}));
EXPECT_EQ(Size(62, 32), host_->GetMinimumSize());
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_FALSE(child2->GetVisible());
EXPECT_TRUE(child3->GetVisible());
EXPECT_EQ(Rect(11, 10, 12, 10), child1->bounds());
EXPECT_EQ(Rect(36, 5, 17, 13), child3->bounds());
child2->ClearProperty(views::kFlexBehaviorKey);
child3->SetProperty(views::kFlexBehaviorKey, kDropOut);
host_->InvalidateLayout();
EXPECT_EQ(Size(77, 32), host_->GetPreferredSize({}));
EXPECT_EQ(Size(58, 32), host_->GetMinimumSize());
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_FALSE(child3->GetVisible());
EXPECT_EQ(Rect(11, 10, 12, 10), child1->bounds());
EXPECT_EQ(Rect(36, 5, 13, 11), child2->bounds());
}
TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropInOrder) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
// Set flex separately; we'll test default flex later.
child1->SetProperty(views::kFlexBehaviorKey, kDropOut);
child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
child3->SetProperty(views::kFlexBehaviorKey, kDropOut);
EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
host_->SetSize(Size(100, 50));
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_TRUE(child3->GetVisible());
host_->SetSize(Size(58, 50));
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_FALSE(child3->GetVisible());
host_->SetSize(Size(57, 50));
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_FALSE(child2->GetVisible());
EXPECT_FALSE(child3->GetVisible());
// Since there's no room for child1, child2 becomes visible.
host_->SetSize(Size(28, 50));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_FALSE(child3->GetVisible());
host_->SetSize(Size(27, 50));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_FALSE(child2->GetVisible());
EXPECT_FALSE(child3->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropInOrder_DefaultFlex) {
// Perform the same test as above but with default flex set instead.
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
layout_->SetDefault(views::kFlexBehaviorKey, kDropOut);
EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
host_->SetSize(Size(100, 50));
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_TRUE(child3->GetVisible());
host_->SetSize(Size(58, 50));
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_FALSE(child3->GetVisible());
host_->SetSize(Size(57, 50));
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_FALSE(child2->GetVisible());
EXPECT_FALSE(child3->GetVisible());
// Since there's no room for child1, child2 becomes visible.
host_->SetSize(Size(28, 50));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_FALSE(child3->GetVisible());
host_->SetSize(Size(27, 50));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_FALSE(child2->GetVisible());
EXPECT_FALSE(child3->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropByPriority) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(kLayoutInsets);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
View* child1 = AddChild(kChild1Size);
View* child2 = AddChild(kChild2Size);
View* child3 = AddChild(kChild3Size);
child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
child2->SetProperty(views::kMarginsKey, Insets(1));
child3->SetProperty(views::kMarginsKey, Insets(2));
layout_->SetDefault(views::kFlexBehaviorKey, kDropOut);
child3->SetProperty(views::kFlexBehaviorKey, kDropOutHighPriority);
EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
host_->SetSize(Size(100, 50));
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_TRUE(child3->GetVisible());
host_->SetSize(Size(65, 50));
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_FALSE(child2->GetVisible());
EXPECT_TRUE(child3->GetVisible());
host_->SetSize(Size(40, 50));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_FALSE(child2->GetVisible());
EXPECT_TRUE(child3->GetVisible());
host_->SetSize(Size(20, 50));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_FALSE(child2->GetVisible());
EXPECT_FALSE(child3->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_Flex_OneViewScales) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(10, 20), Size(5, 5));
View* child2 = AddChild(Size(10, 10));
child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
host_->SetSize(Size(20, 50));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(10, 20), child1->size());
EXPECT_EQ(Size(10, 10), child2->size());
host_->SetSize(Size(20, 35));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(10, 10), child1->size());
EXPECT_EQ(Size(10, 10), child2->size());
host_->SetSize(Size(20, 30));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(10, 5), child1->size());
EXPECT_EQ(Size(10, 10), child2->size());
}
TEST_F(FlexLayoutTest, Layout_Flex_OneViewScales_BelowMinimum) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(10, 20), Size(5, 5));
View* child2 = AddChild(Size(10, 10));
child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
host_->SetSize(Size(20, 20));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(10, 5), child1->size());
EXPECT_EQ(Size(10, 10), child2->size());
}
TEST_F(FlexLayoutTest,
Layout_Flex_OneViewScales_CausesSubsequentControlToDropOut) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(10, 20), Size(5, 5));
View* child2 = AddChild(Size(10, 10), Size(5, 5));
child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
host_->SetSize(Size(20, 20));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(10, 10), child1->size());
EXPECT_FALSE(child2->GetVisible());
}
TEST_F(FlexLayoutTest,
Layout_Flex_OneViewScales_CausesSubsequentFlexControlToDropOut) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(10, 20), Size(5, 5));
View* child2 = AddChild(Size(10, 10), Size(5, 5));
child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
child2->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
host_->SetSize(Size(20, 19));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(10, 9), child1->size());
EXPECT_FALSE(child2->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_EqualWeight) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
layout_->SetDefault(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
View* child1 = AddChild(Size(20, 10), Size(5, 5));
View* child2 = AddChild(Size(20, 10), Size(5, 5));
host_->SetSize(Size(45, 20));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(15, 10), child1->size());
EXPECT_EQ(Size(15, 10), child2->size());
host_->SetSize(Size(60, 20));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(20, 10), child1->size());
EXPECT_EQ(Size(20, 10), child2->size());
}
TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_DefaultFlex) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
layout_->SetDefault(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
View* child1 = AddChild(Size(20, 10), Size(5, 5));
View* child2 = AddChild(Size(20, 10), Size(5, 5));
host_->SetSize(Size(45, 20));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(15, 10), child1->size());
EXPECT_EQ(Size(15, 10), child2->size());
host_->SetSize(Size(60, 20));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(20, 10), child1->size());
EXPECT_EQ(Size(20, 10), child2->size());
}
TEST_F(FlexLayoutTest,
Layout_Flex_TwoChildViews_UnequalWeight_FirstHigher_FlexSmaller) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(20, 10), Size(5, 5));
View* child2 = AddChild(Size(20, 10), Size(5, 5));
child1->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum);
child2->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
// Deficit of 5 is allocated 2:1, but rounding gives us -3, -2.
host_->SetSize(Size(50, 20));
std::vector<gfx::Rect> expected = {{5, 5, 17, 10}, {27, 5, 18, 10}};
EXPECT_EQ(expected, GetChildBounds());
// Deficit of 6 divides evenly, gives us -4, -2.
host_->SetSize(Size(49, 20));
expected = {{5, 5, 16, 10}, {26, 5, 18, 10}};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest,
Layout_Flex_TwoChildViews_UnequalWeight_SecondHigher_FlexSmaller) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(20, 10), Size(5, 5));
View* child2 = AddChild(Size(20, 10), Size(5, 5));
child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
child2->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum);
// Deficit of 5 is allocated 1:2, but rounding gives us -2, -3.
host_->SetSize(Size(50, 20));
std::vector<gfx::Rect> expected = {{5, 5, 18, 10}, {28, 5, 17, 10}};
EXPECT_EQ(expected, GetChildBounds());
// Deficit of 6 divides evenly, gives us -2, -4.
host_->SetSize(Size(49, 20));
expected = {{5, 5, 18, 10}, {28, 5, 16, 10}};
EXPECT_EQ(expected, GetChildBounds());
}
// This is a test for the case where one child's flex rule will cause it to
// scale to its minimum size, resulting in the other view getting more space
// than it otherwise would.
TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_UnequalWeight_OneHitsMinimum) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(20, 10), Size(5, 5));
View* child2 = AddChild(Size(20, 10), Size(5, 5));
child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
child2->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum);
// Deficit of 20 divides up as -7 and -13.
host_->SetSize(Size(35, 20));
std::vector<gfx::Rect> expected = {{5, 5, 13, 10}, {23, 5, 7, 10}};
EXPECT_EQ(expected, GetChildBounds());
// Deficit of 25 divides up as -8 and -17, but second view can only shrink by
// 15, so first view has to shrink by 10 instead.
host_->SetSize(Size(30, 20));
expected = {{5, 5, 10, 10}, {20, 5, 5, 10}};
EXPECT_EQ(expected, GetChildBounds());
}
// This is a test for the case where one child's flex rule will cause it to
// drop out, resulting in the other view getting more space *than its preferred
// size*.
TEST_F(
FlexLayoutTest,
Layout_Flex_TwoChildViews_UnequalWeight_OneDropsOut_OtherExceedsPreferred) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(20, 10), Size(15, 10));
View* child2 = AddChild(Size(20, 10), Size(15, 10));
child1->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToMinimumSnapToZero);
child2->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToMinimumSnapToZero.WithWeight(4));
host_->SetSize(Size(45, 20));
std::vector<gfx::Rect> expected = {{5, 5, 35, 10}, {}};
EXPECT_EQ(expected, GetChildBounds());
}
// This is a regression test for a case where a view marked as having flex
// weight but which could not flex larger than its preferred size would cause
// other views at that weight to not receive available flex space.
TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FirstViewFillsAvailableSpace) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(20, 10));
View* child2 = AddChild(Size(20, 10));
child1->SetProperty(views::kFlexBehaviorKey, kUnbounded);
child2->SetProperty(views::kFlexBehaviorKey,
FlexSpecification(MinimumFlexSizeRule::kPreferred,
MaximumFlexSizeRule::kPreferred));
host_->SetSize(Size(70, 20));
const std::vector<Rect> expected_bounds = {{5, 5, 35, 10}, {45, 5, 20, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
}
TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_Priority) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(20, 10), Size(5, 5));
View* child2 = AddChild(Size(20, 10), Size(5, 5));
child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
child2->SetProperty(views::kFlexBehaviorKey,
kFlex1ScaleToMinimumHighPriority);
host_->SetSize(Size(50, 20));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(15, 10), child1->size());
EXPECT_EQ(Size(20, 10), child2->size());
host_->SetSize(Size(35, 20));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(5, 10), child1->size());
EXPECT_EQ(Size(15, 10), child2->size());
}
TEST_F(FlexLayoutTest,
Layout_Flex_TwoChildViews_Priority_LowerPriorityDropsOut) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(20, 10), Size(5, 5));
View* child2 = AddChild(Size(20, 10), Size(5, 5));
child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
child2->SetProperty(views::kFlexBehaviorKey,
kFlex1ScaleToMinimumHighPriority);
host_->SetSize(Size(35, 20));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(20, 10), child2->size());
EXPECT_FALSE(child1->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedSnapToMinimum) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child = AddChild(Size(20, 10), Size(5, 5));
child->SetProperty(views::kFlexBehaviorKey, kUnboundedSnapToMinimum);
host_->SetSize(Size(35, 25));
EXPECT_EQ(Size(25, 15), child->size());
host_->SetSize(Size(30, 25));
EXPECT_EQ(Size(20, 15), child->size());
host_->SetSize(Size(29, 25));
EXPECT_EQ(Size(5, 15), child->size());
host_->SetSize(Size(25, 10));
EXPECT_EQ(Size(5, 5), child->size());
// This is actually less space than the child needs, but its flex rule does
// not allow it to drop out.
host_->SetSize(Size(10, 10));
EXPECT_EQ(Size(5, 5), child->size());
}
TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToMinimumSnapToZero) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child = AddChild(Size(20, 10), Size(5, 5));
child->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToMinimumSnapToZero);
host_->SetSize(Size(35, 25));
EXPECT_EQ(Size(25, 15), child->size());
host_->SetSize(Size(30, 25));
EXPECT_EQ(Size(20, 15), child->size());
host_->SetSize(Size(29, 25));
EXPECT_EQ(Size(19, 15), child->size());
host_->SetSize(Size(25, 16));
EXPECT_EQ(Size(15, 6), child->size());
// This is too short to display the view, however it has horizontal size, so
// the view does not drop out.
host_->SetSize(Size(25, 10));
EXPECT_TRUE(child->GetVisible());
EXPECT_EQ(Size(15, 0), child->size());
host_->SetSize(Size(15, 15));
EXPECT_EQ(Size(5, 5), child->size());
host_->SetSize(Size(14, 15));
EXPECT_FALSE(child->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToZero) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
// Because we are using a flex rule that scales all the way to zero, ensure
// that the child view's minimum size is *not* respected.
View* child = AddChild(Size(20, 10), Size(5, 5));
child->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZero);
host_->SetSize(Size(35, 25));
EXPECT_EQ(Size(25, 15), child->size());
host_->SetSize(Size(30, 25));
EXPECT_EQ(Size(20, 15), child->size());
host_->SetSize(Size(29, 25));
EXPECT_EQ(Size(19, 15), child->size());
host_->SetSize(Size(25, 16));
EXPECT_EQ(Size(15, 6), child->size());
// This is too short to display the view, however it has horizontal size, so
// the view does not drop out.
host_->SetSize(Size(25, 10));
EXPECT_TRUE(child->GetVisible());
EXPECT_EQ(Size(15, 0), child->size());
host_->SetSize(Size(15, 15));
EXPECT_EQ(Size(5, 5), child->size());
host_->SetSize(Size(14, 14));
EXPECT_EQ(Size(4, 4), child->size());
host_->SetSize(Size(9, 14));
EXPECT_FALSE(child->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedSnapToMinimum1D) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child = AddChild(Size(20, 10), Size(5, 5));
child->SetProperty(views::kFlexBehaviorKey,
kUnboundedSnapToMinimumHorizontal);
host_->SetSize(Size(35, 25));
EXPECT_EQ(Size(25, 10), child->size());
host_->SetSize(Size(30, 25));
EXPECT_EQ(Size(20, 10), child->size());
host_->SetSize(Size(29, 25));
EXPECT_EQ(Size(5, 10), child->size());
host_->SetSize(Size(25, 10));
EXPECT_EQ(Size(5, 10), child->size());
// This is actually less space than the child needs, but its flex rule does
// not allow it to drop out.
host_->SetSize(Size(10, 10));
EXPECT_EQ(Size(5, 10), child->size());
}
TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToMinimumSnapToZero1D) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child = AddChild(Size(20, 10), Size(5, 5));
child->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToMinimumSnapToZeroHorizontal);
host_->SetSize(Size(35, 25));
EXPECT_EQ(Size(25, 10), child->size());
host_->SetSize(Size(30, 25));
EXPECT_EQ(Size(20, 10), child->size());
host_->SetSize(Size(29, 25));
EXPECT_EQ(Size(19, 10), child->size());
host_->SetSize(Size(25, 16));
EXPECT_EQ(Size(15, 10), child->size());
host_->SetSize(Size(25, 10));
EXPECT_TRUE(child->GetVisible());
EXPECT_EQ(Size(15, 10), child->size());
host_->SetSize(Size(15, 15));
EXPECT_EQ(Size(5, 10), child->size());
host_->SetSize(Size(14, 15));
EXPECT_FALSE(child->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToZero1D) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
// Because we are using a flex rule that scales all the way to zero, ensure
// that the child view's minimum size is *not* respected.
View* child = AddChild(Size(20, 10), Size(5, 5));
child->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZeroHorizontal);
host_->SetSize(Size(35, 25));
EXPECT_EQ(Size(25, 10), child->size());
host_->SetSize(Size(30, 25));
EXPECT_EQ(Size(20, 10), child->size());
host_->SetSize(Size(29, 25));
EXPECT_EQ(Size(19, 10), child->size());
host_->SetSize(Size(25, 16));
EXPECT_EQ(Size(15, 10), child->size());
host_->SetSize(Size(25, 10));
EXPECT_TRUE(child->GetVisible());
EXPECT_EQ(Size(15, 10), child->size());
host_->SetSize(Size(15, 15));
EXPECT_EQ(Size(5, 10), child->size());
host_->SetSize(Size(14, 14));
EXPECT_EQ(Size(4, 10), child->size());
host_->SetSize(Size(9, 14));
EXPECT_FALSE(child->GetVisible());
}
// Tests that views allowed to scale up to their maximum size will do so.
TEST_F(FlexLayoutTest, Layout_FlexRule_ScaleToMaximum) {
auto* const child1 = AddChild(Size(10, 10));
child1->SetMaximumSize(Size(20, 20));
child1->SetProperty(kFlexBehaviorKey, kScaleToMaximum);
auto* const child2 = AddChild(Size(10, 10));
child2->SetMaximumSize(Size(20, 20));
child2->SetProperty(kFlexBehaviorKey, kScaleToMaximum);
auto* const child3 = AddChild(Size(10, 10));
child3->SetMaximumSize(Size(20, 20));
child3->SetProperty(kFlexBehaviorKey, kScaleToMaximum);
host_->SetSize(Size(20, 10));
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected_bounds = {
{0, 0, 10, 10}, {10, 0, 10, 10}, {20, 0, 10, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
host_->SetSize(Size(30, 10));
test::RunScheduledLayout(host_.get());
expected_bounds = {{0, 0, 10, 10}, {10, 0, 10, 10}, {20, 0, 10, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
host_->SetSize(Size(33, 10));
test::RunScheduledLayout(host_.get());
expected_bounds = {{0, 0, 11, 10}, {11, 0, 11, 10}, {22, 0, 11, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
host_->SetSize(Size(35, 10));
test::RunScheduledLayout(host_.get());
expected_bounds = {{0, 0, 12, 10}, {12, 0, 12, 10}, {24, 0, 11, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
host_->SetSize(Size(60, 10));
test::RunScheduledLayout(host_.get());
expected_bounds = {{0, 0, 20, 10}, {20, 0, 20, 10}, {40, 0, 20, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
host_->SetSize(Size(70, 10));
test::RunScheduledLayout(host_.get());
expected_bounds = {{0, 0, 20, 10}, {20, 0, 20, 10}, {40, 0, 20, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
}
// Tests that views allowed to scale up to their maximum size will do so.
TEST_F(FlexLayoutTest, Layout_FlexRule_ScaleToMaximum_WithOrder) {
auto* const child1 = AddChild(Size(10, 10));
child1->SetMaximumSize(Size(20, 20));
child1->SetProperty(kFlexBehaviorKey, kScaleToMaximum.WithOrder(1));
auto* const child2 = AddChild(Size(10, 10));
child2->SetMaximumSize(Size(20, 20));
child2->SetProperty(kFlexBehaviorKey, kScaleToMaximum.WithOrder(2));
auto* const child3 = AddChild(Size(10, 10));
child3->SetMaximumSize(Size(20, 20));
child3->SetProperty(kFlexBehaviorKey, kScaleToMaximum.WithOrder(3));
host_->SetSize(Size(20, 10));
test::RunScheduledLayout(host_.get());
std::vector<Rect> expected_bounds = {
{0, 0, 10, 10}, {10, 0, 10, 10}, {20, 0, 10, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
host_->SetSize(Size(30, 10));
test::RunScheduledLayout(host_.get());
expected_bounds = {{0, 0, 10, 10}, {10, 0, 10, 10}, {20, 0, 10, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
host_->SetSize(Size(33, 10));
test::RunScheduledLayout(host_.get());
expected_bounds = {{0, 0, 13, 10}, {13, 0, 10, 10}, {23, 0, 10, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
host_->SetSize(Size(43, 10));
test::RunScheduledLayout(host_.get());
expected_bounds = {{0, 0, 20, 10}, {20, 0, 13, 10}, {33, 0, 10, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
host_->SetSize(Size(53, 10));
test::RunScheduledLayout(host_.get());
expected_bounds = {{0, 0, 20, 10}, {20, 0, 20, 10}, {40, 0, 13, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
host_->SetSize(Size(70, 10));
test::RunScheduledLayout(host_.get());
expected_bounds = {{0, 0, 20, 10}, {20, 0, 20, 10}, {40, 0, 20, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
}
// A higher priority view which can expand past its maximum size should displace
// a lower priority view up to the first view's preferred size.
TEST_F(FlexLayoutTest,
Layout_FlexRule_TwoPassScaling_PreferredSizeTakesPrecedence) {
constexpr Size kLargeSize(10, 10);
constexpr Size kSmallSize(5, 5);
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
View* child1 = AddChild(kLargeSize, kSmallSize);
child1->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToMinimumHighPriority);
View* child2 = AddChild(kSmallSize);
child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
// When there is no room for the second view, it drops out.
host_->SetSize(Size(4, 5));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(kSmallSize, child1->size());
EXPECT_FALSE(child2->GetVisible());
// When the first view has less room than its preferred size, it should still
// take up all of the space.
constexpr Size kIntermediateSize(8, 7);
host_->SetSize(kIntermediateSize);
test::RunScheduledLayout(host_.get());
EXPECT_EQ(kIntermediateSize, child1->size());
EXPECT_FALSE(child2->GetVisible());
// When the first view has more room than its preferred size, but not enough
// to make room for the second view, the second view still drops out.
constexpr Size kLargerSize(13, 8);
host_->SetSize(kLargerSize);
test::RunScheduledLayout(host_.get());
EXPECT_EQ(kLargerSize, child1->size());
EXPECT_FALSE(child2->GetVisible());
}
// When a view is allowed to flex above its preferred size, it will still yield
// that additional space to a lower-priority view, if there is space for the
// second view.
TEST_F(FlexLayoutTest, Layout_FlexRule_TwoPassScaling_StopAtPreferredSize) {
constexpr Size kLargeSize(10, 10);
constexpr Size kSmallSize(5, 5);
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
View* child1 = AddChild(kLargeSize, kSmallSize);
child1->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToMinimumHighPriority);
View* child2 = AddChild(kSmallSize);
child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
constexpr Size kEnoughSpace(kSmallSize.width() + kLargeSize.width(),
kLargeSize.height());
host_->SetSize(kEnoughSpace);
test::RunScheduledLayout(host_.get());
EXPECT_EQ(kLargeSize, child1->size());
EXPECT_EQ(kSmallSize, child2->size());
}
// Once lower-priority views have reached their preferred sizes, a
// higher-priority view which can expand past its preferred size should start to
// consume the remaining space.
TEST_F(FlexLayoutTest, Layout_FlexRule_TwoPassScaling_GrowPastPreferredSize) {
constexpr Size kLargeSize(10, 10);
constexpr Size kSmallSize(5, 5);
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
View* child1 = AddChild(kLargeSize, kSmallSize);
child1->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToMinimumHighPriority);
View* child2 = AddChild(kSmallSize);
child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
constexpr int kExtra = 7;
constexpr Size kExtraSpace(kSmallSize.width() + kLargeSize.width() + kExtra,
kLargeSize.height() + kExtra);
host_->SetSize(kExtraSpace);
EXPECT_EQ(Size(kLargeSize.width() + kExtra, kLargeSize.height() + kExtra),
child1->size());
EXPECT_EQ(kSmallSize, child2->size());
}
// If two views can both scale past their preferred size with the same priority,
// once space has been allocated for each's preferred size, additional space
// will be divided according to flex weight.
TEST_F(FlexLayoutTest,
Layout_FlexRule_GrowPastPreferredSize_TwoViews_SamePriority) {
constexpr Size kLargeSize(10, 10);
constexpr Size kSmallSize(5, 5);
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
// Because we are using a flex rule that scales all the way to zero, ensure
// that the child view's minimum size is *not* respected.
View* child1 = AddChild(kLargeSize, kSmallSize);
child1->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToMinimumHighPriority);
View* child2 = AddChild(kLargeSize, kSmallSize);
child2->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToMinimumHighPriority);
constexpr int kExtra = 8;
constexpr Size kExtraSpace(2 * kLargeSize.width() + kExtra,
kLargeSize.height());
host_->SetSize(kExtraSpace);
EXPECT_EQ(Size(kLargeSize.width() + kExtra / 2, kLargeSize.height()),
child1->size());
EXPECT_EQ(Size(kLargeSize.width() + kExtra / 2, kLargeSize.height()),
child2->size());
}
// If two views can both scale past their preferred size once space has been
// allocated for each's preferred size, additional space will be given to the
// higher-precedence view.
TEST_F(FlexLayoutTest,
Layout_FlexRule_GrowPastPreferredSize_TwoViews_DifferentPriority) {
constexpr Size kLargeSize(10, 10);
constexpr Size kSmallSize(5, 5);
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
// Because we are using a flex rule that scales all the way to zero, ensure
// that the child view's minimum size is *not* respected.
View* child1 = AddChild(kLargeSize, kSmallSize);
child1->SetProperty(views::kFlexBehaviorKey,
kUnboundedScaleToMinimumHighPriority);
View* child2 = AddChild(kLargeSize, kSmallSize);
child2->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToMinimum);
constexpr int kExtra = 8;
constexpr Size kExtraSpace(2 * kLargeSize.width() + kExtra,
kLargeSize.height());
host_->SetSize(kExtraSpace);
EXPECT_EQ(Size(kLargeSize.width() + kExtra, kLargeSize.height()),
child1->size());
EXPECT_EQ(kLargeSize, child2->size());
}
TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FlexAlignment_Start) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(10, 10));
AddChild(Size(10, 10));
child1->SetProperty(views::kFlexBehaviorKey,
kUnbounded.WithAlignment(LayoutAlignment::kStart));
host_->SetSize(Size(50, 20));
const std::vector<Rect> expected_bounds = {{5, 5, 10, 10}, {35, 5, 10, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
}
TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FlexAlignment_End) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(10, 10));
AddChild(Size(10, 10));
child1->SetProperty(views::kFlexBehaviorKey,
kUnbounded.WithAlignment(LayoutAlignment::kEnd));
host_->SetSize(Size(50, 20));
const std::vector<Rect> expected_bounds = {{20, 5, 10, 10}, {35, 5, 10, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
}
TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FlexAlignment_Center) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child1 = AddChild(Size(10, 10));
AddChild(Size(10, 10));
child1->SetProperty(views::kFlexBehaviorKey,
kUnbounded.WithAlignment(LayoutAlignment::kCenter));
host_->SetSize(Size(50, 20));
const std::vector<Rect> expected_bounds = {{12, 5, 10, 10}, {35, 5, 10, 10}};
EXPECT_EQ(expected_bounds, GetChildBounds());
}
TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule) {
constexpr int kFullSize = 50;
constexpr int kHalfSize = 25;
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child = AddChild(Size(kFullSize, kFullSize));
child->SetProperty(views::kFlexBehaviorKey, kCustomFlex);
host_->SetSize(Size(100, 100));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kFullSize, kFullSize), child->size());
host_->SetSize(Size(100, 50));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kFullSize, kHalfSize), child->size());
host_->SetSize(Size(50, 100));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kHalfSize, kFullSize), child->size());
host_->SetSize(Size(45, 40));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kHalfSize, kHalfSize), child->size());
// Custom flex rule does not go below half size.
host_->SetSize(Size(20, 20));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kHalfSize, kHalfSize), child->size());
}
TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule_WithNonFlex) {
constexpr int kFullSize = 50;
constexpr int kHalfSize = 25;
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child = AddChild(Size(kFullSize, kFullSize));
AddChild(Size(10, 10));
child->SetProperty(views::kFlexBehaviorKey, kCustomFlex);
host_->SetSize(Size(100, 100));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kFullSize, kFullSize), child->size());
host_->SetSize(Size(100, 65));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kFullSize, kHalfSize), child->size());
host_->SetSize(Size(50, 100));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kHalfSize, kFullSize), child->size());
host_->SetSize(Size(45, 40));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kHalfSize, kHalfSize), child->size());
}
TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule_ShrinkToZero) {
constexpr int kFullSize = 50;
constexpr int kHalfSize = 25;
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
View* child = AddChild(Size(kFullSize, kFullSize));
child->SetProperty(views::kFlexBehaviorKey, kCustomFlexSnapToZero);
host_->SetSize(Size(100, 100));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kFullSize, kFullSize), child->size());
host_->SetSize(Size(100, 50));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kFullSize, kHalfSize), child->size());
host_->SetSize(Size(50, 100));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kHalfSize, kFullSize), child->size());
host_->SetSize(Size(45, 40));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(Size(kHalfSize, kHalfSize), child->size());
host_->SetSize(Size(20, 20));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child->GetVisible());
}
TEST_F(FlexLayoutTest, Layout_OnlyCallsSetViewVisibilityWhenNecessary) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetInteriorMargin(Insets(5));
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
MockView* child1 = AddChild(Size(20, 10), Size(5, 5));
MockView* child2 = AddChild(Size(20, 10), Size(5, 5));
child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
child2->SetProperty(views::kFlexBehaviorKey,
kFlex1ScaleToMinimumHighPriority);
child1->ResetCounts();
child2->ResetCounts();
host_->SetSize(Size(40, 20));
test::RunScheduledLayout(host_.get());
EXPECT_TRUE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_EQ(0, child1->GetSetVisibleCount());
EXPECT_EQ(0, child2->GetSetVisibleCount());
host_->SetSize(Size(35, 20));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_EQ(1, child1->GetSetVisibleCount());
EXPECT_EQ(0, child2->GetSetVisibleCount());
child1->ResetCounts();
child2->ResetCounts();
host_->SetSize(Size(30, 20));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_EQ(0, child1->GetSetVisibleCount());
EXPECT_EQ(0, child2->GetSetVisibleCount());
child1->SetVisible(false);
child1->ResetCounts();
host_->SetSize(Size(40, 20));
test::RunScheduledLayout(host_.get());
EXPECT_FALSE(child1->GetVisible());
EXPECT_TRUE(child2->GetVisible());
EXPECT_EQ(0, child1->GetSetVisibleCount());
EXPECT_EQ(0, child2->GetSetVisibleCount());
}
TEST_F(FlexLayoutTest, Layout_Vertical_ZeroWidthNonZeroHeight) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
layout_->SetDefault(kMarginsKey, gfx::Insets(5));
layout_->SetCollapseMargins(true);
AddChild(gfx::Size(10, 10));
AddChild(gfx::Size(0, 10));
AddChild(gfx::Size(10, 10));
host_->SetSize(gfx::Size(20, 50));
const std::vector<gfx::Rect> expected = {
{5, 5, 10, 10}, {10, 20, 0, 10}, {5, 35, 10, 10}};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, Layout_Vertical_ZeroWidthZeroHeight) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
layout_->SetDefault(kMarginsKey, gfx::Insets(5));
layout_->SetCollapseMargins(true);
AddChild(gfx::Size(10, 10));
AddChild(gfx::Size(0, 0));
AddChild(gfx::Size(10, 10));
host_->SetSize(gfx::Size(20, 40));
const std::vector<gfx::Rect> expected = {{5, 5, 10, 10}, {}, {5, 20, 10, 10}};
EXPECT_EQ(expected, GetChildBounds());
}
// Available Size Tests -------------------------------------------------------
TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
MockView* const child1 = AddChild(Size(20, 10));
MockView* const child2 = AddChild(Size(10, 5));
// With no flex at preferred size views will get their preferred main axis
// size.
host_->SizeToPreferredSize();
EXPECT_EQ(SizeBounds(20, 10), host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(10, 10), host_->GetAvailableSize(child2));
}
TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex_Margins) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
MockView* const child1 = AddChild(Size(20, 10));
child1->SetProperty(views::kMarginsKey, gfx::Insets::TLBR(3, 5, 7, 5));
MockView* const child2 = AddChild(Size(10, 5));
child2->SetProperty(views::kMarginsKey, gfx::Insets::TLBR(9, 5, 5, 5));
host_->SizeToPreferredSize();
EXPECT_EQ(SizeBounds(20, 10), host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(10, 6), host_->GetAvailableSize(child2));
}
TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex_ExtraSize) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
MockView* const child1 = AddChild(Size(20, 10));
MockView* const child2 = AddChild(Size(10, 5));
host_->SetSize({50, 25});
const int excess = host_->width() - host_->GetPreferredSize({}).width();
EXPECT_EQ(SizeBounds(child1->GetPreferredSize({}).width() + excess, 15),
host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(child2->GetPreferredSize({}).width() + excess, 15),
host_->GetAvailableSize(child2));
}
TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex_Vertical) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
MockView* const child1 = AddChild(Size(20, 10));
MockView* const child2 = AddChild(Size(10, 5));
host_->SetSize({35, 35});
const int excess = host_->height() - host_->GetPreferredSize({}).height();
EXPECT_EQ(SizeBounds(25, child1->GetPreferredSize({}).height() + excess),
host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(25, child2->GetPreferredSize({}).height() + excess),
host_->GetAvailableSize(child2));
}
TEST_F(FlexLayoutTest, GetAvailableSize_Flex_AllSameSize) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
MockView* const child1 = AddChild(Size(20, 10));
MockView* const child2 = AddChild(Size(10, 5));
MockView* const child3 = AddChild(Size(5, 5));
child3->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);
MockView* const child4 = AddChild(Size(5, 5));
child4->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);
host_->SizeToPreferredSize();
// Each of these views can expand into both the view space and the margins for
// the third and fourth views (total 10 each).
EXPECT_EQ(SizeBounds(40, 10), host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(30, 10), host_->GetAvailableSize(child2));
// Each of these can expand into the view space and margin of the other.
EXPECT_EQ(SizeBounds(15, 10), host_->GetAvailableSize(child3));
EXPECT_EQ(SizeBounds(15, 10), host_->GetAvailableSize(child4));
// At minimum size there should be no excess available.
host_->SetSize(host_->GetMinimumSize());
EXPECT_EQ(SizeBounds(20, 10), host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(10, 10), host_->GetAvailableSize(child2));
EXPECT_EQ(SizeBounds(0, 10), host_->GetAvailableSize(child3));
EXPECT_EQ(SizeBounds(0, 10), host_->GetAvailableSize(child4));
}
TEST_F(FlexLayoutTest, GetAvailableSize_Flex_VariedMinimumSizes) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
MockView* const child1 = AddChild(Size(20, 10));
MockView* const child2 = AddChild(Size(10, 5));
MockView* const child3 = AddChild(Size(10, 10), Size(7, 7));
child3->SetProperty(kFlexBehaviorKey, kUnboundedScaleToMinimum);
MockView* const child4 = AddChild(Size(12, 12), Size(5, 5));
child4->SetProperty(kFlexBehaviorKey, kUnboundedScaleToMinimum);
host_->SizeToPreferredSize();
// Since the third and fourth views can only shrink a certain amount, the
// excess available to the first two views is smaller than in previous tests.
EXPECT_EQ(SizeBounds(30, 12), host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(20, 12), host_->GetAvailableSize(child2));
// Each of these can only consume the difference between the other's minimum
// and preferred sizes (as per the flex rule).
EXPECT_EQ(SizeBounds(17, 12), host_->GetAvailableSize(child3));
EXPECT_EQ(SizeBounds(15, 12), host_->GetAvailableSize(child4));
// At minimum size there should be no excess available.
host_->SetSize(host_->GetMinimumSize());
EXPECT_EQ(SizeBounds(20, 10), host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(10, 10), host_->GetAvailableSize(child2));
EXPECT_EQ(SizeBounds(7, 10), host_->GetAvailableSize(child3));
EXPECT_EQ(SizeBounds(5, 10), host_->GetAvailableSize(child4));
}
TEST_F(FlexLayoutTest, GetAvailableSize_Flex_HiddenViews) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
MockView* const child1 = AddChild(Size(20, 10));
MockView* const child2 = AddChild(Size(10, 5));
MockView* const child3 = AddChild(Size(5, 5));
child3->SetProperty(kFlexBehaviorKey, kDropOut);
MockView* const child4 = AddChild(Size(5, 5));
child4->SetProperty(kFlexBehaviorKey, kDropOut);
// Make the second child invisible. This should exclude it from the layout.
child2->SetVisible(false);
host_->SizeToPreferredSize();
// This view can expand into both the view space and the margins for the third
// and fourth views (total 10 each).
EXPECT_EQ(SizeBounds(40, 10), host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(), host_->GetAvailableSize(child2));
// These views can expand into each others' space.
EXPECT_EQ(SizeBounds(15, 10), host_->GetAvailableSize(child3));
EXPECT_EQ(SizeBounds(15, 10), host_->GetAvailableSize(child4));
// This should cause one of the third or fourth children to drop out, but both
// will still have some space available.
host_->SetSize({40, 20});
EXPECT_EQ(SizeBounds(30, 10), host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(), host_->GetAvailableSize(child2));
EXPECT_EQ(SizeBounds(5, 10), host_->GetAvailableSize(child3));
EXPECT_EQ(SizeBounds(5, 10), host_->GetAvailableSize(child4));
// At minimum size, there is no space for the third or fourth view.
host_->SetSize(host_->GetMinimumSize());
EXPECT_EQ(SizeBounds(20, 10), host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(), host_->GetAvailableSize(child2));
EXPECT_EQ(SizeBounds(0, 10), host_->GetAvailableSize(child3));
EXPECT_EQ(SizeBounds(0, 10), host_->GetAvailableSize(child4));
}
TEST_F(FlexLayoutTest, GetAvailableSize_Flex_DifferentWeights) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
MockView* const child1 = AddChild(Size(20, 10));
MockView* const child2 = AddChild(Size(10, 5));
MockView* const child3 = AddChild(Size(12, 12), Size(7, 7));
child3->SetProperty(kFlexBehaviorKey, kFlex1ScaleToMinimumHighPriority);
MockView* const child4 = AddChild(Size(8, 8), Size(4, 4));
child4->SetProperty(kFlexBehaviorKey, kFlex1ScaleToMinimum);
host_->SetSize({80, 25});
const int excess = host_->width() - host_->GetPreferredSize({}).width();
const int child3_excess =
child3->GetPreferredSize({}).width() - child3->GetMinimumSize().width();
const int child4_excess =
child4->GetPreferredSize({}).width() - child4->GetMinimumSize().width();
// The first two views can take all of the excess plus the difference between
// minimum and preferred size for each of the third and fourth views.
EXPECT_EQ(SizeBounds(child1->GetPreferredSize({}).width() + excess +
child3_excess + child4_excess,
15),
host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(child2->GetPreferredSize({}).width() + excess +
child3_excess + child4_excess,
15),
host_->GetAvailableSize(child2));
// The third view has a higher priority, so it can take the excess plus the
// excess from the fourth view.
EXPECT_EQ(
SizeBounds(child3->GetPreferredSize({}).width() + excess + child4_excess,
15),
host_->GetAvailableSize(child3));
// This view has the lowest priority so it can only take the excess space in
// the layout.
EXPECT_EQ(SizeBounds(child4->GetPreferredSize({}).width() + excess, 15),
host_->GetAvailableSize(child4));
// Same as above, but there is no overall excess.
host_->SizeToPreferredSize();
EXPECT_EQ(SizeBounds(child1->GetPreferredSize({}).width() + child3_excess +
child4_excess,
12),
host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(child2->GetPreferredSize({}).width() + child3_excess +
child4_excess,
12),
host_->GetAvailableSize(child2));
EXPECT_EQ(
SizeBounds(child3->GetPreferredSize({}).width() + child4_excess, 12),
host_->GetAvailableSize(child3));
EXPECT_EQ(SizeBounds(child4->GetPreferredSize({}).width(), 12),
host_->GetAvailableSize(child4));
// At minimum size there is no excess; all views get their minimum size.
host_->SetSize(host_->GetMinimumSize());
EXPECT_EQ(SizeBounds(child1->GetPreferredSize({}).width(), 10),
host_->GetAvailableSize(child1));
EXPECT_EQ(SizeBounds(child2->GetPreferredSize({}).width(), 10),
host_->GetAvailableSize(child2));
EXPECT_EQ(SizeBounds(child3->GetMinimumSize().width(), 10),
host_->GetAvailableSize(child3));
EXPECT_EQ(SizeBounds(child4->GetMinimumSize().width(), 10),
host_->GetAvailableSize(child4));
}
// Flex Allocation Order -------------------------------------------------------
TEST_F(FlexLayoutTest, FlexAllocationOrderNormal) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetDefault(kFlexBehaviorKey, kDropOut.WithWeight(0));
layout_->SetFlexAllocationOrder(FlexAllocationOrder::kNormal);
View* const v1 = AddChild({10, 10});
View* const v2 = AddChild({10, 10});
View* const v3 = AddChild({10, 10});
host_->SetSize({35, 10});
EXPECT_TRUE(v1->GetVisible());
EXPECT_TRUE(v2->GetVisible());
EXPECT_TRUE(v3->GetVisible());
host_->SetSize({25, 10});
EXPECT_TRUE(v1->GetVisible());
EXPECT_TRUE(v2->GetVisible());
EXPECT_FALSE(v3->GetVisible());
host_->SetSize({15, 10});
EXPECT_TRUE(v1->GetVisible());
EXPECT_FALSE(v2->GetVisible());
EXPECT_FALSE(v3->GetVisible());
}
TEST_F(FlexLayoutTest, FlexAllocationOrderReverse) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetDefault(kFlexBehaviorKey, kDropOut.WithWeight(0));
layout_->SetFlexAllocationOrder(FlexAllocationOrder::kReverse);
View* const v1 = AddChild({10, 10});
View* const v2 = AddChild({10, 10});
View* const v3 = AddChild({10, 10});
host_->SetSize({35, 10});
EXPECT_TRUE(v1->GetVisible());
EXPECT_TRUE(v2->GetVisible());
EXPECT_TRUE(v3->GetVisible());
host_->SetSize({25, 10});
EXPECT_FALSE(v1->GetVisible());
EXPECT_TRUE(v2->GetVisible());
EXPECT_TRUE(v3->GetVisible());
host_->SetSize({15, 10});
EXPECT_FALSE(v1->GetVisible());
EXPECT_FALSE(v2->GetVisible());
EXPECT_TRUE(v3->GetVisible());
}
TEST_F(FlexLayoutTest, FlexAllocationOrderNormalWithExcess) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetDefault(kFlexBehaviorKey, kUnbounded.WithWeight(0));
layout_->SetFlexAllocationOrder(FlexAllocationOrder::kNormal);
View* const v1 = AddChild({10, 10});
View* const v2 = AddChild({10, 10});
View* const v3 = AddChild({10, 10});
host_->SetSize({30, 10});
EXPECT_EQ(gfx::Size(10, 10), v1->size());
EXPECT_EQ(gfx::Size(10, 10), v2->size());
EXPECT_EQ(gfx::Size(10, 10), v3->size());
host_->SetSize({50, 10});
EXPECT_EQ(gfx::Size(30, 10), v1->size());
EXPECT_EQ(gfx::Size(10, 10), v2->size());
EXPECT_EQ(gfx::Size(10, 10), v3->size());
}
TEST_F(FlexLayoutTest, FlexAllocationOrderReverseWithExcess) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetDefault(kFlexBehaviorKey, kUnbounded.WithWeight(0));
layout_->SetFlexAllocationOrder(FlexAllocationOrder::kReverse);
View* const v1 = AddChild({10, 10});
View* const v2 = AddChild({10, 10});
View* const v3 = AddChild({10, 10});
host_->SetSize({30, 10});
EXPECT_EQ(gfx::Size(10, 10), v1->size());
EXPECT_EQ(gfx::Size(10, 10), v2->size());
EXPECT_EQ(gfx::Size(10, 10), v3->size());
host_->SetSize({50, 10});
EXPECT_EQ(gfx::Size(10, 10), v1->size());
EXPECT_EQ(gfx::Size(10, 10), v2->size());
EXPECT_EQ(gfx::Size(30, 10), v3->size());
}
// Specific Regression Cases ---------------------------------------------------
// Test case (and example code) for crbug.com/1012119:
// "FlexLayout ignores custom flex rule if it contradicts preferred size"
TEST_F(FlexLayoutTest, FlexRuleContradictsPreferredSize) {
const FlexSpecification custom_spec(
base::BindRepeating([](const View* view, const SizeBounds& maximum_size) {
return gfx::Size((maximum_size.width() >= 100) ? 100 : 0, 100);
}));
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
View* const v1 = AddChild(gfx::Size(7, 7));
View* const v2 = AddChild(gfx::Size(7, 7));
v1->SetProperty(kFlexBehaviorKey, custom_spec.WithOrder(1));
v2->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);
host_->SetSize({200, 100});
std::vector<gfx::Rect> expected = {{0, 0, 100, 100}, {100, 0, 100, 100}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({101, 100});
expected = {{0, 0, 100, 100}, {100, 0, 1, 100}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({100, 100});
expected = {{0, 0, 100, 100}, {}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({99, 100});
expected = {{}, {0, 0, 99, 100}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({1, 100});
expected = {{}, {0, 0, 1, 100}};
EXPECT_EQ(expected, GetChildBounds());
}
// Test case (and example code) for crbug.com/1012136:
// "FlexLayout makes children with preferred main axis size 0 invisible even if
// they are kUnbounded"
TEST_F(FlexLayoutTest, PreferredSizeZeroPreventsFlex_Horizontal) {
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
AddChild(gfx::Size(10, 10));
View* const v1 = AddChild(gfx::Size(0, 10));
View* const v2 = AddChild(gfx::Size(0, 10));
v1->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);
v2->SetProperty(kFlexBehaviorKey, kUnboundedSnapToZero);
host_->SetSize({30, 15});
std::vector<gfx::Rect> expected = {
{0, 0, 10, 10}, {10, 0, 10, 15}, {20, 0, 10, 15}};
EXPECT_EQ(expected, GetChildBounds());
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
test::RunScheduledLayout(host_.get());
expected = {{0, 0, 10, 15}, {10, 0, 10, 15}, {20, 0, 10, 15}};
EXPECT_EQ(expected, GetChildBounds());
}
// Test case (and example code) for crbug.com/1012136:
// "FlexLayout makes children with preferred main axis size 0 invisible even if
// they are kUnbounded"
TEST_F(FlexLayoutTest, PreferredSizeZeroPreventsFlex_Vertical) {
layout_->SetOrientation(LayoutOrientation::kVertical);
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
AddChild(gfx::Size(10, 10));
View* const v1 = AddChild(gfx::Size(10, 0));
View* const v2 = AddChild(gfx::Size(10, 0));
v1->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);
v2->SetProperty(kFlexBehaviorKey, kUnboundedSnapToZero);
host_->SetSize({15, 30});
std::vector<gfx::Rect> expected = {
{0, 0, 10, 10}, {0, 10, 15, 10}, {0, 20, 15, 10}};
EXPECT_EQ(expected, GetChildBounds());
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
test::RunScheduledLayout(host_.get());
expected = {{0, 0, 15, 10}, {0, 10, 15, 10}, {0, 20, 15, 10}};
EXPECT_EQ(expected, GetChildBounds());
}
// Test case should be fixed by:
// https://chromium-review.googlesource.com/c/chromium/src/+/2420128
// Specifically, a label in a flex layout should report preferred height of
// |max_lines| * |line_height| when width is zero, which should affect the
// height bound of the layout.
TEST_F(FlexLayoutTest, LabelPreferredHeightChangesWithWidth) {
// A LayoutProvider must exist in scope in order to create a Label.
LayoutProvider layout_provider;
layout_->SetOrientation(LayoutOrientation::kHorizontal);
AddChild(gfx::Size(20, 20));
Label* const label = host_->AddChildView(std::make_unique<Label>());
label->SetMultiLine(true);
label->SetMaxLines(2);
label->SetLineHeight(20);
label->SetProperty(kFlexBehaviorKey,
FlexSpecification(LayoutOrientation::kHorizontal,
MinimumFlexSizeRule::kScaleToZero,
MaximumFlexSizeRule::kPreferred,
/*use_height_for_width*/ true));
// Use a long text string with lots of small words that will wrap.
label->SetText(u"The quick brown fox jumps over the lazy dogs.");
// Verify that when there is enough space, the label takes up a single line.
host_->SizeToPreferredSize();
EXPECT_EQ(20, host_->height());
EXPECT_EQ(20, label->height());
// Now shrink the layout enough that the line should wrap.
const int new_width = host_->width() - 20;
const int new_height = host_->GetHeightForWidth(new_width);
EXPECT_EQ(new_height, 40);
host_->SetSize(gfx::Size(new_width, new_height));
EXPECT_EQ(40, label->height());
}
// Regression test for crbug.com/1239888:
// A vertical layout nested in a horizontal layout should be laid out properly.
// Specifically, it should get its full height if its child views need to grow
// vertically if they are compressed horizontally.
TEST_F(FlexLayoutTest, VerticalInHorizontalInVertical_HeightForWidth) {
constexpr gfx::Size kChildSize(10, 10);
auto* const horizontal = host_->AddChildView(std::make_unique<views::View>());
auto* view1 = AddChild(horizontal, kChildSize);
auto* const vertical =
horizontal->AddChildView(std::make_unique<views::View>());
auto* const view2 = AddChild(vertical, kChildSize);
view2->set_size_mode(MockView::SizeMode::kFixedArea);
auto* const view3 = AddChild(vertical, kChildSize);
view3->set_size_mode(MockView::SizeMode::kFixedArea);
auto* const view4 = AddChild(kChildSize);
layout_->SetOrientation(LayoutOrientation::kVertical);
horizontal->SetProperty(
kFlexBehaviorKey,
FlexSpecification(
horizontal->SetLayoutManager(std::make_unique<FlexLayout>())
->SetOrientation(LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(LayoutAlignment::kStart)
.GetDefaultFlexRule()));
vertical->SetProperty(
kFlexBehaviorKey,
FlexSpecification(
vertical->SetLayoutManager(std::make_unique<FlexLayout>())
->SetOrientation(LayoutOrientation::kVertical)
.GetDefaultFlexRule()));
const views::FlexSpecification view_flex(
views::LayoutOrientation::kVertical,
views::MinimumFlexSizeRule::kPreferred,
views::MaximumFlexSizeRule::kPreferred,
/* adjust_height_for_width = */ true,
views::MinimumFlexSizeRule::kScaleToZero);
view2->SetProperty(kFlexBehaviorKey, view_flex);
view3->SetProperty(kFlexBehaviorKey, view_flex);
EXPECT_EQ(gfx::Size(20, 30), host_->GetPreferredSize({}));
EXPECT_EQ(40, horizontal->GetHeightForWidth(15));
EXPECT_EQ(50, host_->GetHeightForWidth(15));
host_->SetSize(gfx::Size(15, 50));
EXPECT_EQ(gfx::Rect(0, 0, 15, 40), horizontal->bounds());
EXPECT_EQ(gfx::Rect(0, 40, 15, 10), view4->bounds());
EXPECT_EQ(gfx::Rect(0, 0, 10, 10), view1->bounds());
EXPECT_EQ(gfx::Rect(10, 0, 5, 40), vertical->bounds());
EXPECT_EQ(gfx::Rect(0, 0, 5, 20), view2->bounds());
EXPECT_EQ(gfx::Rect(0, 20, 5, 20), view3->bounds());
}
// Pixel-Perfect/Advanced Tests ------------------------------------------------
// NOTE: these are tests ensuring the quasi-multipass behavior of FlexLayout
// (i.e. special-case handling when views do not take up exactly as much space
// as they are offered and need to have the excess redistributed).
namespace {
// Flex rule that steps each dimension by 5.
Size StepwiseFlexRule(int step,
const View* view,
const SizeBounds& maximum_size) {
DCHECK_GT(step, 0);
Size preferred = view->GetPreferredSize({});
if (maximum_size.width().is_bounded()) {
const int rounded = step * (maximum_size.width().value() / step);
preferred.SetToMax({rounded, 0});
}
if (maximum_size.height().is_bounded()) {
const int rounded = step * (maximum_size.height().value() / step);
preferred.SetToMax({0, rounded});
}
return preferred;
}
} // namespace
// When a view does not take all of the space granted it when excess space is
// being distributed (views flexing above their preferred size) the remaining
// space should be distributed to other views at that flex order.
TEST_F(FlexLayoutTest, Advanced_ViewDoesNotTakeFullExcess_Reallocation) {
layout_->SetOrientation(LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(LayoutAlignment::kStart)
.SetCollapseMargins(true)
.SetDefault(kMarginsKey, gfx::Insets(5));
View* const v1 = AddChild(gfx::Size(10, 10));
View* const v2 = AddChild(gfx::Size(10, 10));
// By putting the view that expands stepwise after the one that can scale
// smoothly but at the same priority, we test the ability of the layout to
// detect that the stepwise view isn't using all of its space and
// redistribute that back to the first view.
v1->SetProperty(kFlexBehaviorKey,
FlexSpecification(MinimumFlexSizeRule::kPreferred,
MaximumFlexSizeRule::kUnbounded));
v2->SetProperty(kFlexBehaviorKey,
FlexSpecification(base::BindRepeating(&StepwiseFlexRule, 5)));
// All views at preferred size.
host_->SetSize({35, 20});
std::vector<gfx::Rect> expected = {{5, 5, 10, 10}, {20, 5, 10, 10}};
EXPECT_EQ(expected, GetChildBounds());
// Small increase goes to the first view since the second can't use it.
host_->SetSize({37, 20});
expected = {{5, 5, 12, 10}, {22, 5, 10, 10}};
EXPECT_EQ(expected, GetChildBounds());
// This would be enough to step up the second view but it needs to be
// distributed over both views, so the second view still can't use it.
host_->SetSize({40, 20});
expected = {{5, 5, 15, 10}, {25, 5, 10, 10}};
EXPECT_EQ(expected, GetChildBounds());
// There is finally enough space to allocate to the second view that it can
// increase its size.
host_->SetSize({45, 20});
expected = {{5, 5, 15, 10}, {25, 5, 15, 10}};
EXPECT_EQ(expected, GetChildBounds());
}
// When preferred size of views is zero and adding in zero-size views in the
// "allocate excess flex space" phase would put us above the total available
// size, none of them should show.
TEST_F(FlexLayoutTest, Advanced_PreferredSizeZero_AllOrNothing) {
const FlexSpecification spec_scale = FlexSpecification(
MinimumFlexSizeRule::kScaleToZero, MaximumFlexSizeRule::kUnbounded);
layout_->SetOrientation(LayoutOrientation::kVertical)
.SetCrossAxisAlignment(LayoutAlignment::kStart)
.SetCollapseMargins(true)
.SetDefault(kFlexBehaviorKey, spec_scale)
.SetDefault(kMarginsKey, gfx::Insets(5));
View* const v1 = AddChild(gfx::Size(10, 0));
View* const v2 = AddChild(gfx::Size(10, 0));
host_->SetSize({20, 15});
EXPECT_FALSE(v1->GetVisible());
EXPECT_FALSE(v2->GetVisible());
std::vector<gfx::Rect> expected = {{}, {}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({20, 16});
EXPECT_FALSE(v1->GetVisible());
EXPECT_FALSE(v2->GetVisible());
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({20, 17});
EXPECT_TRUE(v1->GetVisible());
EXPECT_TRUE(v2->GetVisible());
expected = {{5, 5, 10, 1}, {5, 11, 10, 1}};
EXPECT_EQ(expected, GetChildBounds());
}
// Individual cross-axis alignment test ----------------------------------------
TEST_F(FlexLayoutTest, IndividualCrossAxisAlignmentInHorizontalLayoutTest) {
layout_->SetOrientation(LayoutOrientation::kHorizontal)
.SetDefault(kMarginsKey, gfx::Insets(5));
View* const v1 = AddChild(gfx::Size(10, 10));
v1->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kStart);
View* const v2 = AddChild(gfx::Size(10, 10));
v2->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kCenter);
View* const v3 = AddChild(gfx::Size(10, 10));
v3->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kEnd);
View* const v4 = AddChild(gfx::Size(10, 10));
v4->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kStretch);
View* const v5 = AddChild(gfx::Size(10, 10));
// v5 uses default
host_->SizeToPreferredSize();
EXPECT_EQ(5, v1->y());
EXPECT_EQ(10, v1->height());
EXPECT_EQ(5, v2->y());
EXPECT_EQ(10, v2->height());
EXPECT_EQ(5, v3->y());
EXPECT_EQ(10, v3->height());
EXPECT_EQ(5, v4->y());
EXPECT_EQ(10, v4->height());
EXPECT_EQ(5, v5->y());
EXPECT_EQ(10, v5->height());
// Next try a larger view.
host_->SetSize(gfx::Size(100, 30));
EXPECT_EQ(5, v1->y());
EXPECT_EQ(10, v1->height());
EXPECT_EQ(10, v2->y());
EXPECT_EQ(10, v2->height());
EXPECT_EQ(15, v3->y());
EXPECT_EQ(10, v3->height());
EXPECT_EQ(5, v4->y());
EXPECT_EQ(20, v4->height());
EXPECT_EQ(5, v5->y());
EXPECT_EQ(20, v5->height());
// Move to a smaller view.
host_->SetSize(gfx::Size(100, 12));
EXPECT_EQ(5, v1->y());
EXPECT_EQ(10, v1->height());
EXPECT_EQ(1, v2->y());
EXPECT_EQ(10, v2->height());
EXPECT_EQ(-3, v3->y());
EXPECT_EQ(10, v3->height());
EXPECT_EQ(5, v4->y());
EXPECT_EQ(2, v4->height());
EXPECT_EQ(5, v5->y());
EXPECT_EQ(2, v5->height());
// Change default cross-axis alignment.
layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
test::RunScheduledLayout(host_.get());
// v1-v4 should remain unchanged.
EXPECT_EQ(5, v1->y());
EXPECT_EQ(10, v1->height());
EXPECT_EQ(1, v2->y());
EXPECT_EQ(10, v2->height());
EXPECT_EQ(-3, v3->y());
EXPECT_EQ(10, v3->height());
EXPECT_EQ(5, v4->y());
EXPECT_EQ(2, v4->height());
// Since v5 doesn't have its own alignment set, it should pick up the new
// default.
EXPECT_EQ(1, v5->y());
EXPECT_EQ(10, v5->height());
}
TEST_F(FlexLayoutTest, IndividualCrossAxisAlignmentInVerticalLayoutTest) {
layout_->SetOrientation(LayoutOrientation::kVertical)
.SetDefault(kMarginsKey, gfx::Insets(5));
View* const v1 = AddChild(gfx::Size(10, 10));
v1->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kStart);
View* const v2 = AddChild(gfx::Size(10, 10));
v2->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kCenter);
View* const v3 = AddChild(gfx::Size(10, 10));
v3->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kEnd);
View* const v4 = AddChild(gfx::Size(10, 10));
v4->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kStretch);
View* const v5 = AddChild(gfx::Size(10, 10));
// v5 uses default
host_->SizeToPreferredSize();
EXPECT_EQ(5, v1->x());
EXPECT_EQ(10, v1->width());
EXPECT_EQ(5, v2->x());
EXPECT_EQ(10, v2->width());
EXPECT_EQ(5, v3->x());
EXPECT_EQ(10, v3->width());
EXPECT_EQ(5, v4->x());
EXPECT_EQ(10, v4->width());
EXPECT_EQ(5, v5->x());
EXPECT_EQ(10, v5->width());
// Next try a larger view.
host_->SetSize(gfx::Size(30, 100));
EXPECT_EQ(5, v1->x());
EXPECT_EQ(10, v1->width());
EXPECT_EQ(10, v2->x());
EXPECT_EQ(10, v2->width());
EXPECT_EQ(15, v3->x());
EXPECT_EQ(10, v3->width());
EXPECT_EQ(5, v4->x());
EXPECT_EQ(20, v4->width());
EXPECT_EQ(5, v5->x());
EXPECT_EQ(20, v5->width());
// Move to a smaller view.
host_->SetSize(gfx::Size(12, 100));
EXPECT_EQ(5, v1->x());
EXPECT_EQ(10, v1->width());
EXPECT_EQ(1, v2->x());
EXPECT_EQ(10, v2->width());
EXPECT_EQ(-3, v3->x());
EXPECT_EQ(10, v3->width());
EXPECT_EQ(5, v4->x());
EXPECT_EQ(2, v4->width());
EXPECT_EQ(5, v5->x());
EXPECT_EQ(2, v5->width());
// Change default cross-axis alignment.
layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
test::RunScheduledLayout(host_.get());
// v1-v4 should remain unchanged.
EXPECT_EQ(5, v1->x());
EXPECT_EQ(10, v1->width());
EXPECT_EQ(1, v2->x());
EXPECT_EQ(10, v2->width());
EXPECT_EQ(-3, v3->x());
EXPECT_EQ(10, v3->width());
EXPECT_EQ(5, v4->x());
EXPECT_EQ(2, v4->width());
// Since v5 doesn't have its own alignment set, it should pick up the new
// default.
EXPECT_EQ(1, v5->x());
EXPECT_EQ(10, v5->width());
}
TEST_F(FlexLayoutTest, PreferredSizeMutationTest) {
layout_->SetOrientation(LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(views::LayoutAlignment::kStart)
.SetIgnoreDefaultMainAxisMargins(true);
// We want to specialize the maximum size and have different sizes within the
// constraints of the scene
const FlexSpecification custom_spec(
base::BindRepeating([](const View* view, const SizeBounds& maximum_size) {
// This custom rule looks strange, but it is constrained by the current
// multi-line label using GetPreferredSize(const SizeBounds&
// available_size). eg: HelpBubbleView. This is indeed the case. Here is
// a simple simulation.
return gfx::Size(maximum_size.width() >= 300
? 300
: maximum_size.width().min_of(250),
24);
}));
View* const v1 = AddChild(gfx::Size(10, 24));
v1->SetProperty(kFlexBehaviorKey, custom_spec.WithOrder(2));
View* const v2 = AddChild(gfx::Size(24, 24));
v2->SetProperty(kFlexBehaviorKey,
kUnbounded.WithAlignment(views::LayoutAlignment::kEnd));
EXPECT_EQ(gfx::Size(324, 24), host_->GetPreferredSize({}));
host_->SizeToPreferredSize();
std::vector<Rect> expected = {{0, 0, 300, 24}, {300, 0, 24, 24}};
EXPECT_EQ(expected, GetChildBounds());
host_->SetSize({300, 24});
expected = {{0, 0, 250, 24}, {276, 0, 24, 24}};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, PreferredSizeMutationTest2) {
layout_->SetOrientation(LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(views::LayoutAlignment::kStart)
.SetIgnoreDefaultMainAxisMargins(true);
// We want to specialize the maximum size and have different sizes within the
// constraints of the scene
const FlexSpecification custom_spec(
base::BindRepeating([](const View* view, const SizeBounds& maximum_size) {
// This custom rule looks strange, but it is constrained by the current
// multi-line label using GetPreferredSize(const SizeBounds&
// available_size). eg: HelpBubbleView. This is indeed the case. Here is
// a simple simulation.
return gfx::Size(maximum_size.width() >= 300
? 300
: maximum_size.width().min_of(250),
24);
}));
View* const v1 = AddChild(gfx::Size(10, 24));
v1->SetProperty(kFlexBehaviorKey, custom_spec.WithOrder(2));
View* const v2 = AddChild(gfx::Size(24, 24));
v2->SetProperty(kFlexBehaviorKey,
kUnbounded.WithAlignment(views::LayoutAlignment::kCenter));
View* const v3 = AddChild(gfx::Size(10, 24));
v3->SetProperty(kFlexBehaviorKey, custom_spec.WithOrder(2));
EXPECT_EQ(gfx::Size(624, 24), host_->GetPreferredSize({}));
host_->SizeToPreferredSize();
std::vector<Rect> expected = {
{0, 0, 300, 24}, {300, 0, 24, 24}, {324, 0, 300, 24}};
EXPECT_EQ(expected, GetChildBounds());
// Test using critical value 300
host_->SetSize({300, 24});
expected = {{0, 0, 138, 24}, {138, 0, 24, 24}, {162, 0, 138, 24}};
EXPECT_EQ(expected, GetChildBounds());
// Test using critical value 524, It comes from the critical value 250+250+24
// Values in [524, 622.5) should behave the same.
//
// Why is it 622.5? Because when the space less than 1.5 (300+300+24-1.5) is
// evenly distributed, the available space of v1 will become 300 due to
// rounding.
host_->SetSize({524, 24});
expected = {{0, 0, 250, 24}, {250, 0, 24, 24}, {274, 0, 250, 24}};
EXPECT_EQ(expected, GetChildBounds());
// Test using critical value 623
host_->SetSize({623, 24});
expected = {{0, 0, 300, 24}, {324, 0, 24, 24}, {373, 0, 250, 24}};
EXPECT_EQ(expected, GetChildBounds());
}
TEST_F(FlexLayoutTest, ZeroPreferedSizeView) {
layout_->SetOrientation(LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(views::LayoutAlignment::kStart)
.SetDefault(views::kMarginsKey, gfx::Insets::VH(0, 5))
.SetIgnoreDefaultMainAxisMargins(true);
View* const v1 = AddChild(gfx::Size(10, 24));
View* const v2 = AddChild(gfx::Size(0, 24));
v2->SetProperty(
kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kScaleToMaximum));
View* const v3 = AddChild(gfx::Size(24, 24));
EXPECT_EQ(gfx::Size(44, 24), host_->GetPreferredSize({}));
host_->SizeToPreferredSize();
std::vector<Rect> expected = {{0, 0, 10, 24}, {0, 0, 0, 0}, {20, 0, 24, 24}};
EXPECT_EQ(expected, GetChildBounds());
EXPECT_TRUE(v1->GetVisible());
EXPECT_FALSE(v2->GetVisible());
EXPECT_TRUE(v3->GetVisible());
}
// Cross-axis Fit Tests --------------------------------------------------------
// Tests for cross-axis alignment that checks three different conditions:
// - child1 fits entirely in the space provided, with margins
// - child2 fits in the space, but its margins don't
// - child3 does not fit in the space provided
class FlexLayoutCrossAxisFitTest : public FlexLayoutTest {
public:
void SetUp() override {
FlexLayoutTest::SetUp();
DCHECK(child_views_.empty());
for (size_t i = 0; i < kNumChildren; ++i) {
View* const child = AddChild(kChildSizes[i]);
child->SetProperty(kMarginsKey, gfx::Insets(kChildMargins[i]));
child_views_.push_back(child);
}
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout_->SetCollapseMargins(true);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
host_->SetSize(kHostSize);
}
void TearDown() override { child_views_.clear(); }
protected:
static constexpr size_t kNumChildren = 3;
static constexpr gfx::Size kHostSize = gfx::Size(200, 20);
static constexpr std::array<gfx::Size, kNumChildren> kChildSizes = {
{{10, 10}, {10, 10}, {10, 30}}};
static constexpr std::array<gfx::Insets, kNumChildren> kChildMargins = {
{gfx::Insets::TLBR(6, 0, 2, 0), gfx::Insets::TLBR(10, 0, 5, 0),
gfx::Insets::TLBR(6, 0, 2, 0)}};
std::vector<raw_ptr<View, VectorExperimental>> child_views_;
};
TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossStretch) {
layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
test::RunScheduledLayout(host_.get());
// Expect child views to respect their leading margin and to occupy all
// available space (other than margins), with a minimum size of zero.
for (size_t i = 0; i < kNumChildren; ++i) {
EXPECT_EQ(kChildMargins[i].top(), child_views_[i]->origin().y());
const int expected_height =
std::max(0, kHostSize.height() - kChildMargins[i].height());
EXPECT_EQ(expected_height, child_views_[i]->size().height());
}
}
TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossStart) {
layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
test::RunScheduledLayout(host_.get());
// These should all justify to the leading edge and keep their original size.
for (size_t i = 0; i < kNumChildren; ++i) {
EXPECT_EQ(kChildMargins[i].top(), child_views_[i]->origin().y());
EXPECT_EQ(kChildSizes[i].height(), child_views_[i]->size().height());
}
}
TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossCenter) {
layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
test::RunScheduledLayout(host_.get());
// First child view fits entirely in the host view with margins (18 DIPs).
// The entire height (including margins) will be centered vertically.
int remain = kHostSize.height() -
(kChildSizes[0].height() + kChildMargins[0].height());
int expected = remain / 2 + kChildMargins[0].top();
EXPECT_EQ(expected, child_views_[0]->origin().y());
// Second child view is smaller than the host view, but margins don't fit.
// The margins will be scaled down.
remain = kHostSize.height() - kChildSizes[0].height();
expected =
base::ClampRound(kChildMargins[1].top() * static_cast<float>(remain) /
kChildMargins[1].height());
EXPECT_EQ(expected, child_views_[1]->origin().y());
// Third child view does not fit, so is centered.
remain = kHostSize.height() - kChildSizes[2].height();
expected = std::ceilf(remain * 0.5f);
EXPECT_EQ(expected, child_views_[2]->origin().y());
// Expect child views to retain their preferred sizes.
for (size_t i = 0; i < kNumChildren; ++i) {
EXPECT_EQ(kChildSizes[i].height(), child_views_[i]->size().height());
}
}
TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossEnd) {
layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
test::RunScheduledLayout(host_.get());
// These should all justify to the trailing edge and keep their original size.
for (size_t i = 0; i < kNumChildren; ++i) {
EXPECT_EQ(kHostSize.height() - kChildMargins[i].bottom(),
child_views_[i]->bounds().bottom());
EXPECT_EQ(kChildSizes[i].height(), child_views_[i]->size().height());
}
}
// Nested Layout Tests ---------------------------------------------------------
class NestedFlexLayoutTest : public FlexLayoutTest {
public:
void AddChildren(size_t num_children) {
for (size_t i = 0; i < num_children; ++i) {
auto v = std::make_unique<View>();
FlexLayout* layout = v->SetLayoutManager(std::make_unique<FlexLayout>());
children_.push_back(v.get());
layouts_.push_back(layout);
host_->AddChildView(std::move(v));
}
}
View* AddGrandchild(size_t child_index,
const gfx::Size& preferred,
const std::optional<gfx::Size>& minimum = std::nullopt) {
return AddChild(children_[child_index - 1], preferred, minimum);
}
View* child(size_t child_index) const { return children_[child_index - 1]; }
FlexLayout* layout(size_t child_index) const {
return layouts_[child_index - 1];
}
View* grandchild(size_t child_index, size_t grandchild_index) const {
return children_[child_index - 1]->children()[grandchild_index - 1];
}
private:
std::vector<raw_ptr<FlexLayout, VectorExperimental>> layouts_;
View::Views children_;
};
TEST_F(NestedFlexLayoutTest, SetVisible_UpdatesLayout) {
AddChildren(1);
AddGrandchild(1, gfx::Size(5, 5));
AddGrandchild(1, gfx::Size(5, 5));
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout(1)->SetOrientation(LayoutOrientation::kHorizontal);
EXPECT_EQ(gfx::Size(10, 5), host_->GetPreferredSize({}));
grandchild(1, 1)->SetVisible(false);
EXPECT_EQ(gfx::Size(5, 5), host_->GetPreferredSize({}));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(gfx::Rect(0, 0, 5, 5), child(1)->bounds());
EXPECT_EQ(gfx::Rect(0, 0, 5, 5), grandchild(1, 2)->bounds());
}
TEST_F(NestedFlexLayoutTest, AddChild_UpdatesLayout) {
AddChildren(1);
AddGrandchild(1, gfx::Size(5, 5));
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout(1)->SetOrientation(LayoutOrientation::kHorizontal);
EXPECT_EQ(gfx::Size(5, 5), host_->GetPreferredSize({}));
AddGrandchild(1, gfx::Size(5, 5));
EXPECT_EQ(gfx::Size(10, 5), host_->GetPreferredSize({}));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(gfx::Rect(0, 0, 10, 5), child(1)->bounds());
EXPECT_EQ(gfx::Rect(0, 0, 5, 5), grandchild(1, 1)->bounds());
EXPECT_EQ(gfx::Rect(5, 0, 5, 5), grandchild(1, 2)->bounds());
}
TEST_F(NestedFlexLayoutTest, RemoveChild_UpdatesLayout) {
AddChildren(1);
AddGrandchild(1, gfx::Size(5, 5));
AddGrandchild(1, gfx::Size(5, 5));
layout_->SetOrientation(LayoutOrientation::kHorizontal);
layout(1)->SetOrientation(LayoutOrientation::kHorizontal);
EXPECT_EQ(gfx::Size(10, 5), host_->GetPreferredSize({}));
// Remove one grandchild view, avoiding a memory leak since the view is no
// longer owned.
View* const to_remove = grandchild(1, 2);
child(1)->RemoveChildView(to_remove);
delete to_remove;
EXPECT_EQ(gfx::Size(5, 5), host_->GetPreferredSize({}));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(gfx::Rect(0, 0, 5, 5), child(1)->bounds());
EXPECT_EQ(gfx::Rect(0, 0, 5, 5), grandchild(1, 1)->bounds());
}
TEST_F(NestedFlexLayoutTest, Layout_OppositeOrientation) {
AddChildren(2);
AddGrandchild(1, gfx::Size(5, 5));
AddGrandchild(1, gfx::Size(5, 5));
AddGrandchild(2, gfx::Size(6, 6));
AddGrandchild(2, gfx::Size(6, 6));
layout_->SetOrientation(LayoutOrientation::kHorizontal)
.SetCollapseMargins(false)
.SetCrossAxisAlignment(LayoutAlignment::kStart)
.SetDefault(views::kMarginsKey, gfx::Insets::TLBR(2, 3, 4, 5))
.SetInteriorMargin(gfx::Insets::TLBR(4, 3, 2, 1));
layout(1)
->SetOrientation(LayoutOrientation::kVertical)
.SetCollapseMargins(true)
.SetDefault(views::kMarginsKey, gfx::Insets(2))
.SetInteriorMargin(gfx::Insets(1));
layout(2)
->SetOrientation(LayoutOrientation::kVertical)
.SetCollapseMargins(true)
.SetDefault(views::kMarginsKey, gfx::Insets(1))
.SetInteriorMargin(gfx::Insets(2));
EXPECT_EQ(gfx::Size(39, 29), host_->GetPreferredSize({}));
host_->SetSize(gfx::Size(50, 30));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(gfx::Rect(6, 6, 9, 16), child(1)->bounds());
EXPECT_EQ(gfx::Rect(23, 6, 10, 17), child(2)->bounds());
EXPECT_EQ(gfx::Rect(2, 2, 5, 5), grandchild(1, 1)->bounds());
EXPECT_EQ(gfx::Rect(2, 9, 5, 5), grandchild(1, 2)->bounds());
EXPECT_EQ(gfx::Rect(2, 2, 6, 6), grandchild(2, 1)->bounds());
EXPECT_EQ(gfx::Rect(2, 9, 6, 6), grandchild(2, 2)->bounds());
}
TEST_F(NestedFlexLayoutTest, Layout_SameOrientation) {
AddChildren(2);
AddGrandchild(1, gfx::Size(5, 5));
AddGrandchild(1, gfx::Size(5, 5));
AddGrandchild(2, gfx::Size(6, 6));
AddGrandchild(2, gfx::Size(6, 6));
layout_->SetOrientation(LayoutOrientation::kHorizontal)
.SetCollapseMargins(false)
.SetCrossAxisAlignment(LayoutAlignment::kStart)
.SetDefault(views::kMarginsKey, gfx::Insets::TLBR(2, 3, 4, 5))
.SetInteriorMargin(gfx::Insets::TLBR(4, 3, 2, 1));
layout(1)
->SetOrientation(LayoutOrientation::kHorizontal)
.SetCollapseMargins(true)
.SetDefault(views::kMarginsKey, gfx::Insets(2))
.SetInteriorMargin(gfx::Insets(1));
layout(2)
->SetOrientation(LayoutOrientation::kHorizontal)
.SetCollapseMargins(true)
.SetDefault(views::kMarginsKey, gfx::Insets(1))
.SetInteriorMargin(gfx::Insets(2));
EXPECT_EQ(gfx::Size(53, 22), host_->GetPreferredSize({}));
host_->SetSize(gfx::Size(60, 30));
test::RunScheduledLayout(host_.get());
EXPECT_EQ(gfx::Rect(6, 6, 16, 9), child(1)->bounds());
EXPECT_EQ(gfx::Rect(30, 6, 17, 10), child(2)->bounds());
EXPECT_EQ(gfx::Rect(2, 2, 5, 5), grandchild(1, 1)->bounds());
EXPECT_EQ(gfx::Rect(9, 2, 5, 5), grandchild(1, 2)->bounds());
EXPECT_EQ(gfx::Rect(2, 2, 6, 6), grandchild(2, 1)->bounds());
EXPECT_EQ(gfx::Rect(9, 2, 6, 6), grandchild(2, 2)->bounds());
}
TEST_F(NestedFlexLayoutTest, Layout_Flex) {
AddChildren(2);
AddGrandchild(1, gfx::Size(5, 5));
AddGrandchild(1, gfx::Size(5, 5));
AddGrandchild(2, gfx::Size(6, 6));
AddGrandchild(2, gfx::Size(6, 6));
layout_->SetOrientation(LayoutOrientation::kHorizontal)
.SetCollapseMargins(true)
.SetCrossAxisAlignment(LayoutAlignment::kStart)
.SetDefault(views::kMarginsKey, gfx::Insets(2))
.SetInteriorMargin(gfx::Insets(2));
child(1)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
child(2)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
layout(1)
->SetOrientation(LayoutOrientation::kHorizontal)
.SetCollapseMargins(true)
.SetDefault(views::kMarginsKey, gfx::Insets(2))
.SetInteriorMargin(gfx::Insets(2));
grandchild(1, 1)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
grandchild(1, 2)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
layout(2)
->SetOrientation(LayoutOrientation::kHorizontal)
.SetCollapseMargins(true)
.SetDefault(views::kMarginsKey, gfx::Insets(2))
.SetInteriorMargin(gfx::Insets(2));
grandchild(2, 1)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
grandchild(2, 2)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
EXPECT_EQ(gfx::Size(40, 14), host_->GetPreferredSize({}));
host_->SetSize(gfx::Size(26, 15));
EXPECT_EQ(gfx::Rect(2, 2, 9, 9), child(1)->bounds());
EXPECT_EQ(gfx::Rect(13, 2, 11, 10), child(2)->bounds());
EXPECT_EQ(gfx::Rect(2, 2, 2, 5), grandchild(1, 1)->bounds());
EXPECT_EQ(gfx::Rect(6, 2, 1, 5), grandchild(1, 2)->bounds());
EXPECT_EQ(gfx::Rect(2, 2, 3, 6), grandchild(2, 1)->bounds());
EXPECT_EQ(gfx::Rect(7, 2, 2, 6), grandchild(2, 2)->bounds());
host_->SetSize(gfx::Size(22, 15));
EXPECT_EQ(gfx::Rect(2, 2, 7, 9), child(1)->bounds());
EXPECT_EQ(gfx::Rect(11, 2, 9, 10), child(2)->bounds());
EXPECT_TRUE(grandchild(1, 1)->GetVisible());
EXPECT_FALSE(grandchild(1, 2)->GetVisible());
EXPECT_EQ(gfx::Rect(2, 2, 3, 5), grandchild(1, 1)->bounds());
EXPECT_EQ(gfx::Rect(2, 2, 2, 6), grandchild(2, 1)->bounds());
EXPECT_EQ(gfx::Rect(6, 2, 1, 6), grandchild(2, 2)->bounds());
host_->SetSize(gfx::Size(20, 15));
EXPECT_EQ(gfx::Rect(2, 2, 6, 9), child(1)->bounds());
EXPECT_EQ(gfx::Rect(10, 2, 8, 10), child(2)->bounds());
EXPECT_TRUE(grandchild(1, 1)->GetVisible());
EXPECT_FALSE(grandchild(1, 2)->GetVisible());
EXPECT_EQ(gfx::Rect(2, 2, 2, 5), grandchild(1, 1)->bounds());
EXPECT_EQ(gfx::Rect(2, 2, 1, 6), grandchild(2, 1)->bounds());
EXPECT_EQ(gfx::Rect(5, 2, 1, 6), grandchild(2, 2)->bounds());
}
TEST_F(NestedFlexLayoutTest, UsingDefaultFlexRule) {
AddChildren(2);
AddGrandchild(1, gfx::Size(5, 5));
AddGrandchild(2, gfx::Size(5, 5));
AddGrandchild(2, gfx::Size(5, 5));
child(1)->SetProperty(
kFlexBehaviorKey,
FlexSpecification(layout(1)->GetDefaultFlexRule()).WithOrder(2));
child(2)->SetProperty(kFlexBehaviorKey,
FlexSpecification(layout(2)->GetDefaultFlexRule()));
grandchild(1, 1)->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);
grandchild(2, 1)->SetProperty(kFlexBehaviorKey, kDropOut);
grandchild(2, 2)->SetProperty(kFlexBehaviorKey, kDropOut);
// Extra flex space is allocated to the first child view.
host_->SetSize(gfx::Size(17, 5));
EXPECT_TRUE(child(1)->GetVisible());
EXPECT_EQ(gfx::Size(7, 5), child(1)->size());
EXPECT_TRUE(grandchild(1, 1)->GetVisible());
EXPECT_EQ(gfx::Size(7, 5), grandchild(1, 1)->size());
EXPECT_TRUE(child(2)->GetVisible());
EXPECT_EQ(gfx::Size(10, 5), child(2)->size());
EXPECT_TRUE(grandchild(2, 1)->GetVisible());
EXPECT_EQ(gfx::Size(5, 5), grandchild(2, 1)->size());
EXPECT_TRUE(grandchild(2, 2)->GetVisible());
EXPECT_EQ(gfx::Size(5, 5), grandchild(2, 2)->size());
// Leftover flex space is still allocated to the first child view even after
// one of the grandchildren drops out.
host_->SetSize(gfx::Size(8, 5));
EXPECT_TRUE(child(1)->GetVisible());
EXPECT_EQ(gfx::Size(3, 5), child(1)->size());
EXPECT_TRUE(grandchild(1, 1)->GetVisible());
EXPECT_EQ(gfx::Size(3, 5), grandchild(1, 1)->size());
EXPECT_TRUE(child(2)->GetVisible());
EXPECT_EQ(gfx::Size(5, 5), child(2)->size());
EXPECT_TRUE(grandchild(2, 1)->GetVisible());
EXPECT_EQ(gfx::Size(5, 5), grandchild(2, 1)->size());
EXPECT_FALSE(grandchild(2, 2)->GetVisible());
// Leftover flex space is still allocated to the first child view even after
// two of the grandchildren drop out.
host_->SetSize(gfx::Size(4, 5));
EXPECT_TRUE(child(1)->GetVisible());
EXPECT_EQ(gfx::Size(4, 5), child(1)->size());
EXPECT_TRUE(grandchild(1, 1)->GetVisible());
EXPECT_EQ(gfx::Size(4, 5), grandchild(1, 1)->size());
EXPECT_FALSE(child(2)->GetVisible()) << child(2)->bounds().ToString();
// If there is no leftover space, the first child view is hidden.
host_->SetSize(gfx::Size(5, 5));
EXPECT_FALSE(child(1)->GetVisible());
EXPECT_TRUE(child(2)->GetVisible());
EXPECT_EQ(gfx::Size(5, 5), child(2)->size());
EXPECT_TRUE(grandchild(2, 1)->GetVisible());
EXPECT_EQ(gfx::Size(5, 5), grandchild(2, 1)->size());
EXPECT_FALSE(grandchild(2, 2)->GetVisible());
}
TEST_F(NestedFlexLayoutTest, UnboundedZeroSize) {
AddChildren(1);
child(1)->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZero);
AddGrandchild(1, gfx::Size(0, 5));
grandchild(1, 1)->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZero);
EXPECT_EQ(5, child(1)->GetPreferredSize({}).height());
host_->SetSize(gfx::Size(100, 5));
EXPECT_EQ(5, child(1)->GetPreferredSize({}).height());
test::RunScheduledLayout(host_.get());
EXPECT_EQ(5, child(1)->height());
}
namespace {
struct DirectionalFlexRuleTestParamRules {
const char* const name;
MinimumFlexSizeRule min_main_rule;
MaximumFlexSizeRule max_main_rule;
bool use_height_for_width = false;
MinimumFlexSizeRule min_cross_rule = MinimumFlexSizeRule::kPreferred;
};
struct DirectionalFlexRuleTestParam {
LayoutOrientation orientation;
DirectionalFlexRuleTestParamRules rules;
gfx::Size size;
std::vector<gfx::Rect> expected;
std::string ToString() const {
std::ostringstream oss;
PrintTo(orientation, &oss);
oss << " " << rules.name << " " << size.ToString();
return oss.str();
}
};
// No flex in cross-axis direction.
static const DirectionalFlexRuleTestParamRules kNoCrossFlex = {
"Main unbounded scale to minimum snap to zero, cross no flex.",
MinimumFlexSizeRule::kScaleToMinimumSnapToZero,
MaximumFlexSizeRule::kUnbounded};
// Drop out on main axis, flex on cross axis.
static const DirectionalFlexRuleTestParamRules kMainDropOutCrossFlex = {
"Main preferred snap to zero, cross unbounded scale to zero.",
MinimumFlexSizeRule::kPreferredSnapToZero, MaximumFlexSizeRule::kPreferred,
false, MinimumFlexSizeRule::kScaleToZero};
// Preferred height-for-width on main axis, scale to minimum snap to zero on
// cross axis. (Note: Vertical only!)
static const DirectionalFlexRuleTestParamRules kFlexUseHeightForWidth = {
"Cross scale to minimum snap to zero, main use height for width.",
MinimumFlexSizeRule::kPreferred, MaximumFlexSizeRule::kPreferred, true,
MinimumFlexSizeRule::kScaleToMinimumSnapToZero};
const DirectionalFlexRuleTestParam DirectionalFlexRuleTestParamList[] = {
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(20, 10),
{{0, 0, 10, 10}, {10, 0, 10, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(30, 10),
{{0, 0, 10, 10}, {10, 0, 20, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(16, 10),
{{0, 0, 10, 10}, {10, 0, 6, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(14, 10),
{{0, 0, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(20, 20),
{{0, 5, 10, 10}, {10, 5, 10, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(30, 20),
{{0, 5, 10, 10}, {10, 5, 20, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(16, 20),
{{0, 5, 10, 10}, {10, 5, 6, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(14, 20),
{{0, 5, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(20, 6),
{{0, -2, 10, 10}, {10, -2, 10, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(30, 6),
{{0, -2, 10, 10}, {10, -2, 20, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(16, 6),
{{0, -2, 10, 10}, {10, -2, 6, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(14, 6),
{{0, -2, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(20, 4),
{{0, -3, 10, 10}, {10, -3, 10, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(30, 4),
{{0, -3, 10, 10}, {10, -3, 20, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(16, 4),
{{0, -3, 10, 10}, {10, -3, 6, 10}}},
{LayoutOrientation::kHorizontal,
kNoCrossFlex,
gfx::Size(14, 4),
{{0, -3, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(20, 10),
{{0, 0, 10, 10}, {10, 0, 10, 10}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(30, 10),
{{0, 0, 10, 10}, {10, 0, 10, 10}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(16, 10),
{{0, 0, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(14, 10),
{{0, 0, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(20, 20),
{{0, 5, 10, 10}, {10, 5, 10, 10}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(30, 20),
{{0, 5, 10, 10}, {10, 5, 10, 10}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(16, 20),
{{0, 5, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(14, 20),
{{0, 5, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(20, 6),
{{0, -2, 10, 10}, {10, 0, 10, 6}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(30, 6),
{{0, -2, 10, 10}, {10, 0, 10, 6}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(16, 6),
{{0, -2, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(14, 6),
{{0, -2, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(20, 4),
{{0, -3, 10, 10}, {10, 0, 10, 4}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(30, 4),
{{0, -3, 10, 10}, {10, 0, 10, 4}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(16, 4),
{{0, -3, 10, 10}, {}}},
{LayoutOrientation::kHorizontal,
kMainDropOutCrossFlex,
gfx::Size(14, 4),
{{0, -3, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(10, 20),
{{0, 0, 10, 10}, {0, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(10, 30),
{{0, 0, 10, 10}, {0, 10, 10, 20}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(10, 16),
{{0, 0, 10, 10}, {0, 10, 10, 6}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(10, 14),
{{0, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(20, 20),
{{5, 0, 10, 10}, {5, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(20, 30),
{{5, 0, 10, 10}, {5, 10, 10, 20}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(20, 16),
{{5, 0, 10, 10}, {5, 10, 10, 6}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(20, 14),
{{5, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(6, 20),
{{-2, 0, 10, 10}, {-2, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(6, 30),
{{-2, 0, 10, 10}, {-2, 10, 10, 20}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(6, 16),
{{-2, 0, 10, 10}, {-2, 10, 10, 6}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(6, 14),
{{-2, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(4, 20),
{{-3, 0, 10, 10}, {-3, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(4, 30),
{{-3, 0, 10, 10}, {-3, 10, 10, 20}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(4, 16),
{{-3, 0, 10, 10}, {-3, 10, 10, 6}}},
{LayoutOrientation::kVertical,
kNoCrossFlex,
gfx::Size(4, 14),
{{-3, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(10, 20),
{{0, 0, 10, 10}, {0, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(10, 30),
{{0, 0, 10, 10}, {0, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(10, 16),
{{0, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(10, 14),
{{0, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(20, 20),
{{5, 0, 10, 10}, {5, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(20, 30),
{{5, 0, 10, 10}, {5, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(20, 16),
{{5, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(20, 14),
{{5, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(6, 20),
{{-2, 0, 10, 10}, {0, 10, 6, 10}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(6, 30),
{{-2, 0, 10, 10}, {0, 10, 6, 10}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(6, 16),
{{-2, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(6, 14),
{{-2, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(4, 20),
{{-3, 0, 10, 10}, {0, 10, 4, 10}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(4, 30),
{{-3, 0, 10, 10}, {0, 10, 4, 10}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(4, 16),
{{-3, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kMainDropOutCrossFlex,
gfx::Size(4, 14),
{{-3, 0, 10, 10}, {}}},
{LayoutOrientation::kVertical,
kFlexUseHeightForWidth,
gfx::Size(10, 20),
{{0, 0, 10, 10}, {0, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kFlexUseHeightForWidth,
gfx::Size(20, 20),
{{5, 0, 10, 10}, {5, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kFlexUseHeightForWidth,
gfx::Size(10, 30),
{{0, 0, 10, 10}, {0, 10, 10, 10}}},
{LayoutOrientation::kVertical,
kFlexUseHeightForWidth,
gfx::Size(8, 30),
{{-1, 0, 10, 10}, {0, 10, 8, 12}}},
{LayoutOrientation::kVertical,
kFlexUseHeightForWidth,
gfx::Size(6, 20),
{{-2, 0, 10, 10}, {0, 10, 6, 16}}},
{LayoutOrientation::kVertical,
kFlexUseHeightForWidth,
gfx::Size(4, 20),
{{-3, 0, 10, 10}, {2, 10, 0, 20}}},
};
} // anonymous namespace
class FlexLayoutDirectionalRuleTest
: public FlexLayoutTest,
public testing::WithParamInterface<DirectionalFlexRuleTestParam> {};
TEST_P(FlexLayoutDirectionalRuleTest, TestRules) {
const DirectionalFlexRuleTestParam& param = GetParam();
constexpr gfx::Size kChildSize(10, 10);
constexpr gfx::Size kMinimumSize(5, 5);
layout_->SetOrientation(param.orientation);
layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
AddChild(kChildSize);
MockView* const child2 = AddChild(kChildSize, kMinimumSize);
child2->SetProperty(
kFlexBehaviorKey,
FlexSpecification(param.orientation, param.rules.min_main_rule,
param.rules.max_main_rule,
param.rules.use_height_for_width,
param.rules.min_cross_rule));
if (param.rules.use_height_for_width) {
child2->set_size_mode(MockView::SizeMode::kFixedArea);
}
host_->SetSize(param.size);
EXPECT_EQ(param.expected, GetChildBounds())
<< " Params: " << param.ToString();
}
INSTANTIATE_TEST_SUITE_P(,
FlexLayoutDirectionalRuleTest,
testing::ValuesIn(DirectionalFlexRuleTestParamList));
} // namespace views