| /* | |
| * Copyright 2026 Rive | |
| */ | |
| // A scroll registers itself on each child provider and each child runs it from | |
| // its own updateConstraints. A participant provides through its | |
| // LayoutParticipant child, so the host is not a provider and neither half | |
| // happened for it — participants sat still while layout children scrolled. | |
| // Mirrors rive_core/test/scroll_participant_test.dart. | |
| #include "rive/artboard.hpp" | |
| #include "rive/constraints/scrolling/scroll_constraint.hpp" | |
| #include "rive/layout/layout_participant.hpp" | |
| #include "rive/layout_component.hpp" | |
| #include "rive/shapes/shape.hpp" | |
| #include "rive_file_reader.hpp" | |
| #include "rive_testing.hpp" | |
| #include <catch.hpp> | |
| using namespace rive; | |
| // Asset generated by rive_core/test/layout_participant_export_test.dart: a | |
| // 100-tall viewport over 300-tall content whose only child is a participating | |
| // shape, so nothing else can carry the scroll for it. | |
| TEST_CASE("a scroll constraint moves a participating child", "[layoutscroll]") | |
| { | |
| auto file = ReadRiveFile("assets/layout/scroll_participant.riv"); | |
| auto artboard = file->artboard(); | |
| REQUIRE(artboard != nullptr); | |
| artboard->advance(0.0f); | |
| // By type, not by name: the exporter keeps only the artboard's name, so | |
| // this asset carries none for its children. | |
| auto shapes = artboard->find<Shape>(); | |
| REQUIRE(shapes.size() == 1); | |
| auto* shape = shapes[0]; | |
| REQUIRE(shape->isParticipatingInLayout()); | |
| REQUIRE(artboard->find<ScrollConstraint>().size() == 1); | |
| auto* scroll = artboard->find<ScrollConstraint>()[0]; | |
| // There is room to scroll, so the offset below is not clamped away. | |
| REQUIRE(scroll->maxOffsetY() <= -50.0f); | |
| const float before = shape->worldTransform()[5]; | |
| scroll->scrollOffsetY(-50.0f); | |
| artboard->advance(0.0f); | |
| REQUIRE(shape->worldTransform()[5] - before == Approx(-50.0f)); | |
| } |