blob: 3b1523913cef63489e6f7abecf0ff74d72bb2dce [file] [log] [blame]
#include "rive/animation/state_machine_instance.hpp"
#include "rive/layout/layout_component_style.hpp"
#include "rive/layout/layout_enums.hpp"
#include "rive/math/transform_components.hpp"
#include "rive/shapes/rectangle.hpp"
#include "rive/text/text.hpp"
#include "utils/no_op_factory.hpp"
#include "rive_file_reader.hpp"
#include "rive_testing.hpp"
#include "utils/serializing_factory.hpp"
#include <catch.hpp>
#include <cstdio>
TEST_CASE("LayoutComponent FlexDirection row", "[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_horizontal.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent1") !=
nullptr);
auto target1 = artboard->find<rive::LayoutComponent>("LayoutComponent1");
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent2") !=
nullptr);
auto target2 = artboard->find<rive::LayoutComponent>("LayoutComponent2");
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent3") !=
nullptr);
auto target3 = artboard->find<rive::LayoutComponent>("LayoutComponent3");
artboard->advance(0.0f);
auto target1Components = target1->worldTransform().decompose();
auto target2Components = target2->worldTransform().decompose();
auto target3Components = target3->worldTransform().decompose();
auto style = target1->style();
REQUIRE(style != nullptr);
REQUIRE(style->flexDirection() == YGFlexDirectionRow);
REQUIRE(target1Components.x() == 0);
REQUIRE(target2Components.x() == 100);
REQUIRE(target3Components.x() == 200);
REQUIRE(target1Components.y() == 0);
REQUIRE(target2Components.y() == 0);
REQUIRE(target3Components.y() == 0);
}
TEST_CASE("LayoutComponent FlexDirection column", "[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_vertical.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent1") !=
nullptr);
auto target1 = artboard->find<rive::LayoutComponent>("LayoutComponent1");
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent2") !=
nullptr);
auto target2 = artboard->find<rive::LayoutComponent>("LayoutComponent2");
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent3") !=
nullptr);
auto target3 = artboard->find<rive::LayoutComponent>("LayoutComponent3");
artboard->advance(0.0f);
auto target1Components = target1->worldTransform().decompose();
auto target2Components = target2->worldTransform().decompose();
auto target3Components = target3->worldTransform().decompose();
REQUIRE(target1Components.x() == 0);
REQUIRE(target2Components.x() == 0);
REQUIRE(target3Components.x() == 0);
REQUIRE(target1Components.y() == 0);
REQUIRE(target2Components.y() == 100);
REQUIRE(target3Components.y() == 200);
}
TEST_CASE("LayoutComponent FlexDirection row with gap", "[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_horizontal_gaps.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent1") !=
nullptr);
auto target1 = artboard->find<rive::LayoutComponent>("LayoutComponent1");
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent2") !=
nullptr);
auto target2 = artboard->find<rive::LayoutComponent>("LayoutComponent2");
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent3") !=
nullptr);
auto target3 = artboard->find<rive::LayoutComponent>("LayoutComponent3");
artboard->advance(0.0f);
auto target1Components = target1->worldTransform().decompose();
auto target2Components = target2->worldTransform().decompose();
auto target3Components = target3->worldTransform().decompose();
REQUIRE(target1Components.x() == 0);
REQUIRE(target2Components.x() == 110);
REQUIRE(target3Components.x() == 220);
REQUIRE(target1Components.y() == 0);
REQUIRE(target2Components.y() == 0);
REQUIRE(target3Components.y() == 0);
}
TEST_CASE("LayoutComponent FlexDirection row with wrap", "[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_horizontal_wrap.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent6") !=
nullptr);
auto target = artboard->find<rive::LayoutComponent>("LayoutComponent6");
artboard->advance(0.0f);
auto targetComponents = target->worldTransform().decompose();
REQUIRE(targetComponents.x() == 0);
REQUIRE(targetComponents.y() == 100);
}
TEST_CASE("LayoutComponent Center using alignItems and justifyContent",
"[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_center.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent1") !=
nullptr);
auto target = artboard->find<rive::LayoutComponent>("LayoutComponent1");
artboard->advance(0.0f);
auto targetComponents = target->worldTransform().decompose();
REQUIRE(targetComponents.x() == 200);
REQUIRE(targetComponents.y() == 200);
}
TEST_CASE("LayoutComponent with intrinsic size gets measured correctly",
"[layout]")
{
auto file = ReadRiveFile("assets/layout/measure_tests.riv");
auto artboard = file->artboard("hi");
REQUIRE(artboard->find<rive::LayoutComponent>("TextLayout") != nullptr);
REQUIRE(artboard->find<rive::Text>("HiText") != nullptr);
artboard->advance(0.0f);
auto text = artboard->find<rive::Text>("HiText");
auto bounds = text->localBounds();
REQUIRE(bounds.left() == 0);
REQUIRE(bounds.top() == 0);
REQUIRE(bounds.width() == 62.48047f);
REQUIRE(bounds.height() == 72.62695f);
}
TEST_CASE("LayoutComponent Padding Px", "[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_complex1.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutLeft") != nullptr);
auto parent = artboard->find<rive::LayoutComponent>("LayoutLeft");
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutLeftChild1") !=
nullptr);
auto child1 = artboard->find<rive::LayoutComponent>("LayoutLeftChild1");
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutLeftChild2") !=
nullptr);
auto child2 = artboard->find<rive::LayoutComponent>("LayoutLeftChild2");
artboard->advance(0.0f);
auto style = parent->style();
REQUIRE(style != nullptr);
REQUIRE(style->paddingLeft() == 20);
REQUIRE(style->paddingLeftUnits() == YGUnitPoint);
REQUIRE(style->paddingRight() == 20);
REQUIRE(style->paddingRightUnits() == YGUnitPoint);
REQUIRE(style->paddingTop() == 20);
REQUIRE(style->paddingTopUnits() == YGUnitPoint);
REQUIRE(style->paddingBottom() == 20);
REQUIRE(style->paddingBottomUnits() == YGUnitPoint);
auto parentComponents = parent->worldTransform().decompose();
auto child1Components = child1->worldTransform().decompose();
auto child2Components = child2->worldTransform().decompose();
REQUIRE(parentComponents.x() == 0);
REQUIRE(child1Components.x() == 20);
REQUIRE(child2Components.x() == 130);
REQUIRE(parentComponents.y() == 0);
REQUIRE(child1Components.y() == 20);
REQUIRE(child2Components.y() == 20);
}
TEST_CASE("LayoutComponent Margin Px", "[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_complex1.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutRight") != nullptr);
auto parent = artboard->find<rive::LayoutComponent>("LayoutRight");
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutRightChild1") !=
nullptr);
auto child1 = artboard->find<rive::LayoutComponent>("LayoutRightChild1");
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutRightChild2") !=
nullptr);
auto child2 = artboard->find<rive::LayoutComponent>("LayoutRightChild2");
artboard->advance(0.0f);
auto style1 = child1->style();
REQUIRE(style1 != nullptr);
REQUIRE(style1->marginLeft() == 10);
REQUIRE(style1->marginLeftUnits() == YGUnitPoint);
REQUIRE(style1->marginRight() == 10);
REQUIRE(style1->marginRightUnits() == YGUnitPoint);
REQUIRE(style1->marginTop() == 10);
REQUIRE(style1->marginTopUnits() == YGUnitPoint);
REQUIRE(style1->marginBottom() == 10);
REQUIRE(style1->marginBottomUnits() == YGUnitPoint);
REQUIRE(style1->alignmentType() == rive::LayoutAlignmentType::center);
REQUIRE(style1->flexWrap() == YGWrapNoWrap);
auto style2 = child2->style();
REQUIRE(style2 != nullptr);
REQUIRE(style2->marginLeft() == 5);
REQUIRE(style2->marginLeftUnits() == YGUnitPercent);
REQUIRE(style2->marginRight() == 5);
REQUIRE(style2->marginRightUnits() == YGUnitPercent);
REQUIRE(style2->marginTop() == 5);
REQUIRE(style2->marginTopUnits() == YGUnitPercent);
REQUIRE(style2->marginBottom() == 5);
REQUIRE(style2->marginBottomUnits() == YGUnitPercent);
REQUIRE(style2->alignmentType() == rive::LayoutAlignmentType::topLeft);
REQUIRE(style2->flexWrap() == YGWrapWrap);
auto parentComponents = parent->worldTransform().decompose();
auto child1Components = child1->worldTransform().decompose();
auto child2Components = child2->worldTransform().decompose();
REQUIRE(parentComponents.x() == 250);
REQUIRE(child1Components.x() == 285);
REQUIRE(child2Components.x() == 285);
REQUIRE(parentComponents.y() == 0);
REQUIRE(child1Components.y() == 35);
REQUIRE(child2Components.y() == 215);
}
TEST_CASE("LayoutComponent Corner Radius", "[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_complex1.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutLeftChild1") !=
nullptr);
auto child1 = artboard->find<rive::LayoutComponent>("LayoutLeftChild1");
artboard->advance(0.0f);
auto style = child1->style();
REQUIRE(style != nullptr);
REQUIRE(style->cornerRadiusTL() == 15);
REQUIRE(style->cornerRadiusTR() == 15);
REQUIRE(style->cornerRadiusBL() == 15);
REQUIRE(style->cornerRadiusBR() == 15);
}
TEST_CASE("LayoutComponent Direction", "[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_direction.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("Layout1") != nullptr);
auto target1 = artboard->find<rive::LayoutComponent>("Layout1");
REQUIRE(artboard->find<rive::LayoutComponent>("Layout2") != nullptr);
auto target2 = artboard->find<rive::LayoutComponent>("Layout2");
REQUIRE(artboard->find<rive::LayoutComponent>("Layout3") != nullptr);
auto target3 = artboard->find<rive::LayoutComponent>("Layout3");
REQUIRE(artboard->find<rive::Text>("SampleText") != nullptr);
auto text = artboard->find<rive::Text>("SampleText");
artboard->advance(0.0f);
auto target1Components = target1->worldTransform().decompose();
auto target2Components = target2->worldTransform().decompose();
auto target3Components = target3->worldTransform().decompose();
REQUIRE(target1Components.x() == 200);
REQUIRE(target2Components.x() == 100);
REQUIRE(target3Components.x() == 0);
REQUIRE(target1->actualDirection() == rive::LayoutDirection::rtl);
REQUIRE(target2->actualDirection() == rive::LayoutDirection::rtl);
REQUIRE(target3->actualDirection() == rive::LayoutDirection::rtl);
REQUIRE(text->align() == rive::TextAlign::right);
}
TEST_CASE("LayoutComponent forcedWidth/Height dirt test", "[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_complex1.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutLeftChild1") !=
nullptr);
auto layout = artboard->find<rive::LayoutComponent>("LayoutLeftChild1");
REQUIRE(std::isnan(layout->forcedWidth()));
REQUIRE(std::isnan(layout->forcedHeight()));
layout->forcedWidth(100);
layout->forcedHeight(150);
REQUIRE(layout->forcedWidth() == 100.0f);
REQUIRE(layout->forcedHeight() == 150.0f);
// forcedWidth/Height adds LayoutStyle dirt
REQUIRE(layout->hasDirt(rive::ComponentDirt::LayoutStyle) == true);
artboard->advance(0.0f);
// Advancing clears dirt
REQUIRE(layout->hasDirt(rive::ComponentDirt::LayoutStyle) == false);
layout->forcedWidth(100);
layout->forcedHeight(150);
// Setting the same values should result in no added dirt
REQUIRE(layout->hasDirt(rive::ComponentDirt::LayoutStyle) == false);
}
TEST_CASE("LayoutComponent Alignment", "[layout]")
{
auto file = ReadRiveFile("assets/layout/layout_alignment.riv");
auto artboard = file->artboard();
REQUIRE(artboard->find<rive::LayoutComponent>("LayoutContainer") !=
nullptr);
auto container = artboard->find<rive::LayoutComponent>("LayoutContainer");
REQUIRE(artboard->find<rive::LayoutComponent>("Layout1") != nullptr);
auto layout1 = artboard->find<rive::LayoutComponent>("Layout1");
REQUIRE(artboard->find<rive::LayoutComponent>("Layout2") != nullptr);
auto layout2 = artboard->find<rive::LayoutComponent>("Layout2");
REQUIRE(artboard->find<rive::LayoutComponent>("Layout3") != nullptr);
auto layout3 = artboard->find<rive::LayoutComponent>("Layout3");
auto style = container->style();
REQUIRE(style != nullptr);
// LayoutAlignmentType::spaceBetweenStart
container->style()->layoutAlignmentType(9);
artboard->advance(0.0f);
auto target1Components = layout1->worldTransform().decompose();
auto target2Components = layout2->worldTransform().decompose();
auto target3Components = layout3->worldTransform().decompose();
REQUIRE(target1Components.x() == 0);
REQUIRE(target2Components.x() == 200);
REQUIRE(target3Components.x() == 400);
REQUIRE(target1Components.y() == 0);
REQUIRE(target2Components.y() == 0);
REQUIRE(target3Components.y() == 0);
// LayoutAlignmentType::spaceBetweenCenter
container->style()->layoutAlignmentType(10);
artboard->advance(0.0f);
target1Components = layout1->worldTransform().decompose();
target2Components = layout2->worldTransform().decompose();
target3Components = layout3->worldTransform().decompose();
REQUIRE(target1Components.x() == 0);
REQUIRE(target2Components.x() == 200);
REQUIRE(target3Components.x() == 400);
REQUIRE(target1Components.y() == 200);
REQUIRE(target2Components.y() == 200);
REQUIRE(target3Components.y() == 200);
// YGFlexDirectionColumn
container->style()->flexDirectionValue(0);
artboard->advance(0.0f);
target1Components = layout1->worldTransform().decompose();
target2Components = layout2->worldTransform().decompose();
target3Components = layout3->worldTransform().decompose();
REQUIRE(target1Components.x() == 200);
REQUIRE(target2Components.x() == 200);
REQUIRE(target3Components.x() == 200);
REQUIRE(target1Components.y() == 0);
REQUIRE(target2Components.y() == 200);
REQUIRE(target3Components.y() == 400);
// LayoutAlignmentType::spaceBetweenEnd
container->style()->layoutAlignmentType(11);
artboard->advance(0.0f);
target1Components = layout1->worldTransform().decompose();
target2Components = layout2->worldTransform().decompose();
target3Components = layout3->worldTransform().decompose();
REQUIRE(target1Components.x() == 400);
REQUIRE(target2Components.x() == 400);
REQUIRE(target3Components.x() == 400);
REQUIRE(target1Components.y() == 0);
REQUIRE(target2Components.y() == 200);
REQUIRE(target3Components.y() == 400);
}
TEST_CASE("Prevent Margin Pct on Artboard", "[layout]")
{
auto file = ReadRiveFile("assets/layout/artboard_percent_margin.riv");
auto artboard = file->artboard();
artboard->advance(0.0f);
REQUIRE(artboard->layoutWidth() == 501.0f);
REQUIRE(artboard->layoutHeight() == 512.0f);
}
TEST_CASE("Multiple layout collapsing and soloing in hierarchy.", "[silver]")
{
rive::SerializingFactory silver;
auto file = ReadRiveFile("assets/collapsing_elements.riv", &silver);
auto artboard = file->artboardDefault();
silver.frameSize(artboard->width(), artboard->height());
REQUIRE(artboard != nullptr);
auto stateMachine = artboard->stateMachineAt(0);
stateMachine->advanceAndApply(0.1f);
auto renderer = silver.makeRenderer();
artboard->draw(renderer.get());
int frames = (int)(4.0f / 0.016f);
for (int i = 0; i < frames; i++)
{
silver.addFrame();
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
}
CHECK(silver.matches("collapsing_elements"));
}
TEST_CASE("Animating layout display", "[silver]")
{
rive::SerializingFactory silver;
auto file = ReadRiveFile("assets/layout/layout_display.riv", &silver);
auto artboard = file->artboardDefault();
silver.frameSize(artboard->width(), artboard->height());
REQUIRE(artboard != nullptr);
auto stateMachine = artboard->stateMachineAt(0);
stateMachine->advanceAndApply(0.1f);
auto renderer = silver.makeRenderer();
artboard->draw(renderer.get());
int frames = (int)(1.5f / 0.016f);
for (int i = 0; i < frames; i++)
{
silver.addFrame();
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
}
CHECK(silver.matches("layout_display"));
}
TEST_CASE("Layout background & foreground shape paints.", "[silver]")
{
rive::SerializingFactory silver;
auto file = ReadRiveFile("assets/layout/layout_paint.riv", &silver);
auto artboard = file->artboardDefault();
silver.frameSize(artboard->width(), artboard->height());
REQUIRE(artboard != nullptr);
auto stateMachine = artboard->stateMachineAt(0);
stateMachine->advanceAndApply(0.1f);
auto renderer = silver.makeRenderer();
artboard->draw(renderer.get());
int frames = (int)(2.0f / 0.016f);
for (int i = 0; i < frames; i++)
{
silver.addFrame();
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
}
CHECK(silver.matches("layout_paint"));
}