blob: 9afc0e74b0a20df05c430a0506824e49f591f22f [file]
#include "rive/artboard.hpp"
#include "rive/layout/layout_component_style.hpp"
#include "rive/layout_component.hpp"
#include "rive_file_reader.hpp"
#include "rive_testing.hpp"
#include <catch.hpp>
// Asset generated by rive_core/test/layout_stack_export_test.dart:
// a 400x400 artboard with a 200x200 stack (layoutType=stack, bottomRight
// alignment) containing a fill child and a fixed 40x40 box. Stack synthesizes a
// 1x1 grid, so every child shares the single cell.
TEST_CASE("stack overlaps children and aligns from a .riv file",
"[layoutstack]")
{
auto file = ReadRiveFile("assets/layout/stack.riv");
auto artboard = file->artboard();
REQUIRE(artboard != nullptr);
artboard->advance(0.0f);
// The exporter strips names; identify layouts by structure.
auto layouts = artboard->find<rive::LayoutComponent>();
rive::LayoutComponent* stack = nullptr;
rive::LayoutComponent* fill = nullptr;
rive::LayoutComponent* box = nullptr;
for (auto layout : layouts)
{
if (layout->is<rive::Artboard>() || layout->style() == nullptr)
{
continue;
}
if (layout->style()->isStack())
{
stack = layout;
}
else if (layout->style()->widthScaleType() ==
rive::LayoutScaleType::fill)
{
fill = layout;
}
else
{
box = layout;
}
}
REQUIRE(stack != nullptr);
REQUIRE(fill != nullptr);
REQUIRE(box != nullptr);
// The fill child occupies the entire cell (the whole 200x200 stack),
// overlapping the box — proving both share the single stacked cell.
REQUIRE(fill->layoutX() == 0.0f);
REQUIRE(fill->layoutY() == 0.0f);
REQUIRE(fill->layoutWidth() == 200.0f);
REQUIRE(fill->layoutHeight() == 200.0f);
// The fixed box keeps its size and sits at the bottom-right (alignment).
REQUIRE(box->layoutWidth() == 40.0f);
REQUIRE(box->layoutHeight() == 40.0f);
REQUIRE(box->layoutX() == 160.0f);
REQUIRE(box->layoutY() == 160.0f);
}
// Sweep the 9-way alignment and confirm the fixed box lands in the right spot.
// Exercises start/center/end on both axes (grid uses End, not FlexEnd).
TEST_CASE("stack alignment positions a fixed child", "[layoutstack]")
{
auto file = ReadRiveFile("assets/layout/stack.riv");
auto artboard = file->artboard();
REQUIRE(artboard != nullptr);
artboard->advance(0.0f);
auto layouts = artboard->find<rive::LayoutComponent>();
rive::LayoutComponent* stack = nullptr;
rive::LayoutComponent* box = nullptr;
for (auto layout : layouts)
{
if (layout->is<rive::Artboard>() || layout->style() == nullptr)
{
continue;
}
if (layout->style()->isStack())
{
stack = layout;
}
else if (layout->style()->widthScaleType() !=
rive::LayoutScaleType::fill)
{
box = layout;
}
}
REQUIRE(stack != nullptr);
REQUIRE(box != nullptr);
auto at = [&](rive::LayoutAlignmentType alignment, float x, float y) {
stack->style()->layoutAlignmentType((uint32_t)alignment);
artboard->advance(0.0f);
REQUIRE(box->layoutX() == x);
REQUIRE(box->layoutY() == y);
};
at(rive::LayoutAlignmentType::topLeft, 0.0f, 0.0f);
at(rive::LayoutAlignmentType::topCenter, 80.0f, 0.0f);
at(rive::LayoutAlignmentType::topRight, 160.0f, 0.0f);
at(rive::LayoutAlignmentType::centerLeft, 0.0f, 80.0f);
at(rive::LayoutAlignmentType::center, 80.0f, 80.0f);
at(rive::LayoutAlignmentType::centerRight, 160.0f, 80.0f);
at(rive::LayoutAlignmentType::bottomLeft, 0.0f, 160.0f);
at(rive::LayoutAlignmentType::bottomCenter, 80.0f, 160.0f);
at(rive::LayoutAlignmentType::bottomRight, 160.0f, 160.0f);
}
// display() folds visibility (displayValue) + algorithm (layoutTypeValue) into
// the single YGDisplay the engine consumes: hidden wins, else flex vs grid.
// Uses an attached style (from a loaded file) since the property setters fire
// change handlers that walk to the owning layout.
TEST_CASE("engine display folds visibility and layout type", "[layoutstack]")
{
auto file = ReadRiveFile("assets/layout/stack.riv");
auto artboard = file->artboard();
REQUIRE(artboard != nullptr);
artboard->advance(0.0f);
rive::LayoutComponentStyle* style = nullptr;
for (auto layout : artboard->find<rive::LayoutComponent>())
{
if (!layout->is<rive::Artboard>() && layout->style() != nullptr &&
layout->style()->isStack())
{
style = layout->style();
}
}
REQUIRE(style != nullptr);
style->displayValue(0); // visible
style->layoutTypeValue(0 /* flex */);
REQUIRE(style->display() == YGDisplayFlex);
style->layoutTypeValue(1 /* grid */);
REQUIRE(style->display() == YGDisplayGrid);
style->layoutTypeValue(2 /* stack */);
REQUIRE(style->display() == YGDisplayGrid);
style->displayValue(1); // hidden — wins regardless of type
style->layoutTypeValue(0);
REQUIRE(style->display() == YGDisplayNone);
style->layoutTypeValue(1);
REQUIRE(style->display() == YGDisplayNone);
style->layoutTypeValue(2);
REQUIRE(style->display() == YGDisplayNone);
}