blob: a07b075d01bcc8595ab8087169f807430f810f0e [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/layout/interpolating_layout_manager.h"
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/animation/tween.h"
#include "ui/views/test/test_views.h"
#include "ui/views/view.h"
using namespace views;
namespace {
class TestLayout : public LayoutManagerBase {
public:
explicit TestLayout(int size = 0) : size_(size) {}
int num_layouts_generated() const { return num_layouts_generated_; }
ProposedLayout CalculateProposedLayout(
const SizeBounds& size_bounds) const override {
++num_layouts_generated_;
ProposedLayout layout;
int x = size_;
for (auto it = host_view()->children().begin();
it != host_view()->children().end(); ++it) {
if (!IsChildIncludedInLayout(*it))
continue;
ChildLayout child_layout;
child_layout.child_view = *it;
child_layout.visible = true;
child_layout.bounds = gfx::Rect(x, 1, size_, size_);
layout.child_layouts.push_back(child_layout);
x += size_ + 1;
}
layout.host_size = {x, 2 + size_};
return layout;
}
private:
const int size_;
mutable int num_layouts_generated_ = 0;
};
void CompareProposedLayouts(const ProposedLayout& left,
const ProposedLayout& right) {
EXPECT_EQ(left.host_size, right.host_size);
EXPECT_EQ(left.child_layouts.size(), right.child_layouts.size());
for (auto left_it = left.child_layouts.begin(),
right_it = right.child_layouts.begin();
left_it != left.child_layouts.end() &&
right_it != right.child_layouts.end();
++left_it, ++right_it) {
EXPECT_EQ(left_it->child_view, right_it->child_view);
EXPECT_EQ(left_it->visible, right_it->visible);
if (left_it->visible)
EXPECT_EQ(left_it->bounds, right_it->bounds);
}
}
} // anonymous namespace
class InterpolatingLayoutManagerTest : public testing::Test {
public:
void SetUp() override {
host_view_ = std::make_unique<View>();
layout_manager_ = host_view_->SetLayoutManager(
std::make_unique<InterpolatingLayoutManager>());
}
InterpolatingLayoutManager* layout_manager() { return layout_manager_; }
View* host_view() { return host_view_.get(); }
private:
InterpolatingLayoutManager* layout_manager_ = nullptr;
std::unique_ptr<View> host_view_;
};
TEST_F(InterpolatingLayoutManagerTest, AddLayout) {
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>());
EXPECT_EQ(0, first_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({0, 0});
EXPECT_EQ(1, first_layout->num_layouts_generated());
}
TEST_F(InterpolatingLayoutManagerTest, AddLayout_CheckZeroAndUnbounded) {
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>());
TestLayout* const second_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(), {5, 0});
EXPECT_EQ(0, first_layout->num_layouts_generated());
EXPECT_EQ(0, second_layout->num_layouts_generated());
layout_manager()->GetPreferredSize(host_view());
EXPECT_EQ(0, first_layout->num_layouts_generated());
EXPECT_EQ(1, second_layout->num_layouts_generated());
layout_manager()->GetMinimumSize(host_view());
EXPECT_EQ(1, first_layout->num_layouts_generated());
EXPECT_EQ(1, second_layout->num_layouts_generated());
}
TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout_HardBoundary) {
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>());
TestLayout* const second_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(), {5, 0});
EXPECT_EQ(0, first_layout->num_layouts_generated());
EXPECT_EQ(0, second_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({5, 2});
EXPECT_EQ(0, first_layout->num_layouts_generated());
EXPECT_EQ(1, second_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({4, 2});
EXPECT_EQ(1, first_layout->num_layouts_generated());
EXPECT_EQ(1, second_layout->num_layouts_generated());
}
TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout_SoftBoudnary) {
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>());
TestLayout* const second_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2});
EXPECT_EQ(0, first_layout->num_layouts_generated());
EXPECT_EQ(0, second_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({5, 2});
EXPECT_EQ(1, first_layout->num_layouts_generated());
EXPECT_EQ(1, second_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({4, 2});
EXPECT_EQ(2, first_layout->num_layouts_generated());
EXPECT_EQ(1, second_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({6, 6});
EXPECT_EQ(2, first_layout->num_layouts_generated());
EXPECT_EQ(2, second_layout->num_layouts_generated());
}
TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout_MultipleLayouts) {
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>());
TestLayout* const second_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2});
TestLayout* const third_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(), {6, 2});
EXPECT_EQ(0, first_layout->num_layouts_generated());
EXPECT_EQ(0, second_layout->num_layouts_generated());
EXPECT_EQ(0, third_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({5, 2});
EXPECT_EQ(1, first_layout->num_layouts_generated());
EXPECT_EQ(1, second_layout->num_layouts_generated());
EXPECT_EQ(0, third_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({6, 3});
EXPECT_EQ(1, first_layout->num_layouts_generated());
EXPECT_EQ(2, second_layout->num_layouts_generated());
EXPECT_EQ(0, third_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({7, 6});
EXPECT_EQ(1, first_layout->num_layouts_generated());
EXPECT_EQ(3, second_layout->num_layouts_generated());
EXPECT_EQ(1, third_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({20, 3});
EXPECT_EQ(1, first_layout->num_layouts_generated());
EXPECT_EQ(3, second_layout->num_layouts_generated());
EXPECT_EQ(2, third_layout->num_layouts_generated());
}
TEST_F(InterpolatingLayoutManagerTest, InvalidateLayout) {
static const gfx::Size kLayoutSize(5, 5);
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>());
TestLayout* const second_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2});
host_view()->SetSize(kLayoutSize);
EXPECT_EQ(1, first_layout->num_layouts_generated());
EXPECT_EQ(1, second_layout->num_layouts_generated());
host_view()->Layout();
EXPECT_EQ(1, first_layout->num_layouts_generated());
EXPECT_EQ(1, second_layout->num_layouts_generated());
host_view()->InvalidateLayout();
host_view()->Layout();
EXPECT_EQ(2, first_layout->num_layouts_generated());
EXPECT_EQ(2, second_layout->num_layouts_generated());
host_view()->Layout();
EXPECT_EQ(2, first_layout->num_layouts_generated());
EXPECT_EQ(2, second_layout->num_layouts_generated());
}
TEST_F(InterpolatingLayoutManagerTest, SetOrientation) {
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>());
TestLayout* const second_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2});
layout_manager()->SetOrientation(LayoutOrientation::kVertical);
EXPECT_EQ(0, first_layout->num_layouts_generated());
EXPECT_EQ(0, second_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({2, 6});
EXPECT_EQ(0, first_layout->num_layouts_generated());
EXPECT_EQ(1, second_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({3, 5});
EXPECT_EQ(1, first_layout->num_layouts_generated());
EXPECT_EQ(2, second_layout->num_layouts_generated());
layout_manager()->GetProposedLayout({10, 3});
EXPECT_EQ(2, first_layout->num_layouts_generated());
EXPECT_EQ(2, second_layout->num_layouts_generated());
}
TEST_F(InterpolatingLayoutManagerTest, GetMinimumSize) {
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(5));
layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 5});
// Minimum size should be equal to the default layout.
EXPECT_EQ(first_layout->GetMinimumSize(host_view()),
layout_manager()->GetMinimumSize(host_view()));
}
TEST_F(InterpolatingLayoutManagerTest, GetPreferredSize_NoDefaultLayout) {
layout_manager()->AddLayout(std::make_unique<TestLayout>(5));
TestLayout* const second_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 5});
// Preferred size should be equal to the largest layout.
EXPECT_EQ(second_layout->GetPreferredSize(host_view()),
layout_manager()->GetPreferredSize(host_view()));
}
TEST_F(InterpolatingLayoutManagerTest, GetPreferredSize_UsesDefaultLayout) {
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(5));
layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 5});
layout_manager()->SetDefaultLayout(first_layout);
// Preferred size should be equal to the largest layout.
EXPECT_EQ(first_layout->GetPreferredSize(host_view()),
layout_manager()->GetPreferredSize(host_view()));
}
TEST_F(InterpolatingLayoutManagerTest, GetPreferredHeightForWidth_Vertical) {
layout_manager()->SetOrientation(LayoutOrientation::kVertical);
layout_manager()->AddLayout(std::make_unique<TestLayout>(5));
TestLayout* const second_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 5});
// Vertical means preferred height for width applies to largest layout.
EXPECT_EQ(second_layout->GetPreferredHeightForWidth(host_view(), 7),
layout_manager()->GetPreferredHeightForWidth(host_view(), 7));
EXPECT_EQ(second_layout->GetPreferredHeightForWidth(host_view(), 3),
layout_manager()->GetPreferredHeightForWidth(host_view(), 3));
EXPECT_EQ(second_layout->GetPreferredHeightForWidth(host_view(), 10),
layout_manager()->GetPreferredHeightForWidth(host_view(), 10));
}
TEST_F(InterpolatingLayoutManagerTest, GetPreferredHeightForWidth_Horizontal) {
layout_manager()->SetOrientation(LayoutOrientation::kHorizontal);
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(5));
TestLayout* const second_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 5});
// Horizontal means preferred height for width is interpolated.
// Note that the layout doesn't actually flex height with varying width, so we
// can use constant reference values.
const int default_height =
first_layout->GetPreferredHeightForWidth(host_view(), 7);
const int other_height =
second_layout->GetPreferredHeightForWidth(host_view(), 7);
EXPECT_EQ(default_height,
layout_manager()->GetPreferredHeightForWidth(host_view(), 5));
EXPECT_EQ(other_height,
layout_manager()->GetPreferredHeightForWidth(host_view(), 10));
EXPECT_EQ(int{default_height * 0.4f + other_height * 0.6f},
layout_manager()->GetPreferredHeightForWidth(host_view(), 8));
}
TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout) {
View* const child_view = host_view()->AddChildView(std::make_unique<View>());
TestLayout* const first_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(5));
TestLayout* const second_layout =
layout_manager()->AddLayout(std::make_unique<TestLayout>(10), {5, 6});
constexpr gfx::Size kSmallSize{5, 10};
constexpr gfx::Size kLargeSize{11, 10};
constexpr gfx::Size kOneThirdSize{7, 10};
constexpr gfx::Size kOneHalfSize{8, 10};
const ProposedLayout expected_default =
first_layout->GetProposedLayout(kSmallSize);
const ProposedLayout expected_other =
second_layout->GetProposedLayout(kLargeSize);
CompareProposedLayouts(expected_default,
layout_manager()->GetProposedLayout(kSmallSize));
CompareProposedLayouts(expected_other,
layout_manager()->GetProposedLayout(kLargeSize));
ProposedLayout actual = layout_manager()->GetProposedLayout(kOneThirdSize);
EXPECT_EQ(gfx::Tween::SizeValueBetween(0.3333, expected_default.host_size,
expected_other.host_size),
actual.host_size);
ASSERT_EQ(1U, actual.child_layouts.size());
EXPECT_EQ(child_view, actual.child_layouts[0].child_view);
EXPECT_EQ(true, actual.child_layouts[0].visible);
EXPECT_EQ(gfx::Tween::RectValueBetween(
0.3333, expected_default.child_layouts[0].bounds,
expected_other.child_layouts[0].bounds),
actual.child_layouts[0].bounds);
actual = layout_manager()->GetProposedLayout(kOneHalfSize);
EXPECT_EQ(gfx::Tween::SizeValueBetween(0.5, expected_default.host_size,
expected_other.host_size),
actual.host_size);
ASSERT_EQ(1U, actual.child_layouts.size());
EXPECT_EQ(child_view, actual.child_layouts[0].child_view);
EXPECT_EQ(true, actual.child_layouts[0].visible);
EXPECT_EQ(gfx::Tween::RectValueBetween(
0.5, expected_default.child_layouts[0].bounds,
expected_other.child_layouts[0].bounds),
actual.child_layouts[0].bounds);
}