| /* |
| * 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)); |
| } |