blob: 350d875dea5df16bbcc003e8786dc243b53e3aaf [file] [log] [blame]
// Copyright 2017 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/vr/elements/linear_layout.h"
#include <memory>
#include "chrome/browser/vr/test/animation_utils.h"
#include "chrome/browser/vr/test/constants.h"
#include "chrome/browser/vr/ui_scene.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace vr {
namespace {
// Helper class supplying convenient layout-related accessors.
class TestElement : public UiElement {
public:
float x() const { return world_space_transform().To2dTranslation().x(); }
float y() const { return world_space_transform().To2dTranslation().y(); }
float local_x() const { return LocalTransform().To2dTranslation().x(); }
float local_y() const { return LocalTransform().To2dTranslation().y(); }
};
} // namespace
TEST(LinearLayout, HorizontalVerticalLayout) {
LinearLayout layout(LinearLayout::kRight);
layout.set_margin(10);
auto element = std::make_unique<UiElement>();
UiElement* rect_a = element.get();
rect_a->SetSize(10, 10);
layout.AddChild(std::move(element));
// One element should require no position adjustment at all.
layout.SizeAndLayOut();
EXPECT_TRUE(rect_a->LocalTransform().IsIdentity());
// Two elements should be centered and separated by the margin.
element = std::make_unique<UiElement>();
UiElement* rect_b = element.get();
rect_b->SetSize(10, 10);
rect_b->SetScale(2.0f, 2.0f, 0.0f);
layout.AddChild(std::move(element));
layout.SizeAndLayOut();
gfx::Point3F position_a;
rect_a->LocalTransform().TransformPoint(&position_a);
gfx::Point3F position_b;
rect_b->LocalTransform().TransformPoint(&position_b);
EXPECT_FLOAT_EQ(-15.0f, position_a.x());
EXPECT_FLOAT_EQ(0.0f, position_a.y());
EXPECT_FLOAT_EQ(0.0f, position_a.z());
EXPECT_FLOAT_EQ(10.0f, position_b.x());
EXPECT_FLOAT_EQ(0.0f, position_b.y());
EXPECT_FLOAT_EQ(0.0f, position_b.z());
rect_a->set_requires_layout(false);
layout.SizeAndLayOut();
EXPECT_FLOAT_EQ(20.0f, layout.size().width());
}
TEST(LinearLayout, Alignment) {
LinearLayout layout(LinearLayout::kRight);
layout.set_margin(10);
auto element = std::make_unique<UiElement>();
UiElement* rect_a = element.get();
rect_a->SetSize(1, 1);
layout.AddChild(std::move(element));
element = std::make_unique<UiElement>();
UiElement* rect_b = element.get();
rect_b->SetSize(10, 10);
rect_b->SetScale(2.0f, 2.0f, 0.0f);
layout.AddChild(std::move(element));
gfx::Point3F position_a;
rect_a->set_y_anchoring(TOP);
layout.SizeAndLayOut();
rect_a->LocalTransform().TransformPoint(&position_a);
EXPECT_FLOAT_EQ(9.5f, position_a.y());
position_a = gfx::Point3F();
rect_a->set_y_anchoring(BOTTOM);
layout.SizeAndLayOut();
rect_a->LocalTransform().TransformPoint(&position_a);
EXPECT_FLOAT_EQ(-9.5f, position_a.y());
layout.set_direction(LinearLayout::kLeft);
position_a = gfx::Point3F();
rect_a->set_y_anchoring(TOP);
layout.SizeAndLayOut();
rect_a->LocalTransform().TransformPoint(&position_a);
EXPECT_FLOAT_EQ(9.5f, position_a.y());
position_a = gfx::Point3F();
rect_a->set_y_anchoring(BOTTOM);
layout.SizeAndLayOut();
rect_a->LocalTransform().TransformPoint(&position_a);
EXPECT_FLOAT_EQ(-9.5f, position_a.y());
layout.set_direction(LinearLayout::kDown);
position_a = gfx::Point3F();
rect_a->set_x_anchoring(LEFT);
rect_a->set_y_anchoring(NONE);
layout.SizeAndLayOut();
rect_a->LocalTransform().TransformPoint(&position_a);
EXPECT_FLOAT_EQ(-9.5f, position_a.x());
position_a = gfx::Point3F();
rect_a->set_x_anchoring(RIGHT);
layout.SizeAndLayOut();
rect_a->LocalTransform().TransformPoint(&position_a);
EXPECT_FLOAT_EQ(9.5f, position_a.x());
layout.set_direction(LinearLayout::kUp);
position_a = gfx::Point3F();
rect_a->set_x_anchoring(LEFT);
rect_a->set_y_anchoring(NONE);
layout.SizeAndLayOut();
rect_a->LocalTransform().TransformPoint(&position_a);
EXPECT_FLOAT_EQ(-9.5f, position_a.x());
position_a = gfx::Point3F();
rect_a->set_x_anchoring(RIGHT);
layout.SizeAndLayOut();
rect_a->LocalTransform().TransformPoint(&position_a);
EXPECT_FLOAT_EQ(9.5f, position_a.x());
}
TEST(LinearLayout, Orientations) {
LinearLayout layout(LinearLayout::kUp);
TestElement* rect;
for (int i = 0; i < 2; i++) {
auto element = std::make_unique<TestElement>();
rect = element.get();
element->SetSize(10, 10);
layout.AddChild(std::move(element));
}
layout.set_direction(LinearLayout::kUp);
layout.SizeAndLayOut();
EXPECT_FLOAT_EQ(0.0f, rect->local_x());
EXPECT_FLOAT_EQ(5.0f, rect->local_y());
layout.set_direction(LinearLayout::kDown);
layout.SizeAndLayOut();
EXPECT_FLOAT_EQ(0.0f, rect->local_x());
EXPECT_FLOAT_EQ(-5.0f, rect->local_y());
layout.set_direction(LinearLayout::kLeft);
layout.SizeAndLayOut();
EXPECT_FLOAT_EQ(-5.0f, rect->local_x());
EXPECT_FLOAT_EQ(0.0f, rect->local_y());
layout.set_direction(LinearLayout::kRight);
layout.SizeAndLayOut();
EXPECT_FLOAT_EQ(5.0f, rect->local_x());
EXPECT_FLOAT_EQ(0.0f, rect->local_y());
}
TEST(LinearLayout, NestedLayouts) {
// Build a tree of elements, including nested layouts:
// parent_layout
// child_layout
// rect_a
// rect_b
// rect_c
auto parent_layout = std::make_unique<LinearLayout>(LinearLayout::kDown);
UiElement* p_parent_layout = parent_layout.get();
auto child_layout = std::make_unique<LinearLayout>(LinearLayout::kDown);
UiElement* p_child_layout = child_layout.get();
auto rect_a = std::make_unique<TestElement>();
TestElement* p_rect_a = rect_a.get();
rect_a->SetSize(10, 10);
child_layout->AddChild(std::move(rect_a));
auto rect_b = std::make_unique<TestElement>();
TestElement* p_rect_b = rect_b.get();
rect_b->SetSize(10, 10);
child_layout->AddChild(std::move(rect_b));
auto rect_c = std::make_unique<TestElement>();
TestElement* p_rect_c = rect_c.get();
rect_c->SetSize(999, 10);
parent_layout->AddChild(std::move(child_layout));
parent_layout->AddChild(std::move(rect_c));
auto scene = std::make_unique<UiScene>();
scene->AddUiElement(kRoot, std::move(parent_layout));
scene->OnBeginFrame(MicrosecondsToTicks(1), kStartHeadPose);
// Ensure that layouts expand to include the cumulative size of children.
EXPECT_FLOAT_EQ(p_parent_layout->size().width(), 999.f);
EXPECT_FLOAT_EQ(p_parent_layout->size().height(), 30.f);
EXPECT_FLOAT_EQ(p_child_layout->size().width(), 10.f);
EXPECT_FLOAT_EQ(p_child_layout->size().height(), 20.f);
// Ensure that children are at correct positions.
EXPECT_FLOAT_EQ(p_rect_a->x(), 0);
EXPECT_FLOAT_EQ(p_rect_a->y(), 10);
EXPECT_FLOAT_EQ(p_rect_b->x(), 0);
EXPECT_FLOAT_EQ(p_rect_b->y(), 0);
EXPECT_FLOAT_EQ(p_rect_c->x(), 0);
EXPECT_FLOAT_EQ(p_rect_c->y(), -10);
}
TEST(LinearLayout, SpecifiedMajorExtent) {
auto layout = std::make_unique<LinearLayout>(LinearLayout::kRight);
for (int i = 0; i < 3; i++) {
auto element = std::make_unique<UiElement>();
element->SetSize(1.f, 1.f);
layout->AddChild(std::move(element));
}
LinearLayout* p_layout = layout.get();
UiElement* p_resizable_child = layout->children()[1].get();
auto scene = std::make_unique<UiScene>();
scene->AddUiElement(kRoot, std::move(layout));
scene->OnBeginFrame(MicrosecondsToTicks(0), kStartHeadPose);
EXPECT_FLOAT_EQ(p_layout->size().width(), 3.f);
// Element grows to fit.
p_layout->set_layout_length(3.5f);
p_resizable_child->set_resizable_by_layout(true);
scene->OnBeginFrame(MicrosecondsToTicks(1), kStartHeadPose);
EXPECT_FLOAT_EQ(p_layout->size().width(), 3.5f);
EXPECT_FLOAT_EQ(p_resizable_child->size().width(), 1.5f);
// Element shrinks to fit.
p_layout->set_layout_length(2.5f);
scene->OnBeginFrame(MicrosecondsToTicks(0), kStartHeadPose);
EXPECT_FLOAT_EQ(p_layout->size().width(), 2.5f);
EXPECT_FLOAT_EQ(p_resizable_child->size().width(), 0.5f);
// Element shrinks to 0 if there's no size for it.
p_layout->set_layout_length(1.5f);
scene->OnBeginFrame(MicrosecondsToTicks(0), kStartHeadPose);
EXPECT_FLOAT_EQ(p_layout->size().width(), 2.0f);
EXPECT_FLOAT_EQ(p_resizable_child->size().width(), 0.f);
}
} // namespace vr