| /* |
| * Copyright 2022 Rive |
| */ |
| |
| #include <rive/math/aabb.hpp> |
| #include <rive/math/hit_test.hpp> |
| #include <rive/nested_artboard.hpp> |
| #include <rive/animation/state_machine_instance.hpp> |
| #include <rive/animation/state_machine_input_instance.hpp> |
| #include <rive/animation/nested_state_machine.hpp> |
| #include <rive/animation/animation_state.hpp> |
| #include <rive/viewmodel/viewmodel_instance_number.hpp> |
| #include "rive_file_reader.hpp" |
| #include "utils/serializing_factory.hpp" |
| |
| #include <catch.hpp> |
| #include <cstdio> |
| |
| using namespace rive; |
| |
| TEST_CASE("hittest-basics", "[hittest]") |
| { |
| HitTester tester; |
| tester.reset({10, 10, 12, 12}); |
| tester.move({0, 0}); |
| tester.line({20, 0}); |
| tester.line({20, 20}); |
| tester.line({0, 20}); |
| tester.close(); |
| REQUIRE(tester.test()); |
| |
| IAABB area = {81, 156, 84, 159}; |
| |
| Vec2D pts[] = { |
| {29.9785f, 32.5261f}, |
| {231.102f, 32.5261f}, |
| {231.102f, 269.898f}, |
| {29.9785f, 269.898f}, |
| }; |
| tester.reset(area); |
| |
| tester.move(pts[0]); |
| for (int i = 1; i < 4; ++i) |
| { |
| tester.line(pts[i]); |
| } |
| tester.close(); |
| REQUIRE(tester.test()); |
| } |
| |
| TEST_CASE("hittest-mesh", "[hittest]") |
| { |
| |
| const IAABB area{10, 10, 12, 12}; |
| |
| Vec2D verts[] = { |
| {0, 0}, |
| {20, 10}, |
| {0, 20}, |
| }; |
| uint16_t indices[] = { |
| 0, |
| 1, |
| 2, |
| }; |
| REQUIRE( |
| HitTester::testMesh(area, make_span(verts, 3), make_span(indices, 3))); |
| } |
| |
| TEST_CASE("hit test on opaque target", "[hittest]") |
| { |
| // This artboard has two rects of size 200 x 200, "red-activate" at [0, 0, |
| // 200, 200] and "green-activate" at [0, 100, 200, 300] "red-activate" is |
| // above "green-activate" in drawing order Both targets are set as opaque |
| // for its listeners "red-activate" sets "toGreen" to false "green-activate" |
| // sets "toGreen" to true There is also a "gray-activate" above the other 2 |
| // that is not opaque so events should traverse through the other targets |
| auto file = ReadRiveFile("assets/opaque_hit_test.riv"); |
| |
| auto artboard = file->artboard("main"); |
| auto artboardInstance = artboard->instance(); |
| auto stateMachine = artboard->stateMachine("main-state-machine"); |
| |
| REQUIRE(artboardInstance != nullptr); |
| REQUIRE(artboardInstance->stateMachineCount() == 1); |
| |
| REQUIRE(stateMachine != nullptr); |
| |
| rive::StateMachineInstance* stateMachineInstance = |
| new rive::StateMachineInstance(stateMachine, artboardInstance.get()); |
| |
| stateMachineInstance->advance(0.0f); |
| artboardInstance->advance(0.0f); |
| REQUIRE(stateMachineInstance->needsAdvance() == true); |
| stateMachineInstance->advance(0.0f); |
| |
| auto toGreenToggle = stateMachineInstance->getBool("toGreen"); |
| REQUIRE(toGreenToggle != nullptr); |
| auto grayToggle = stateMachineInstance->getBool("grayToggle"); |
| REQUIRE(grayToggle != nullptr); |
| |
| stateMachineInstance->pointerDown(rive::Vec2D(100.0f, 50.0f)); |
| // "gray-activate" is clicked |
| REQUIRE(grayToggle->value() == true); |
| // Pointer only over "red-activate" |
| REQUIRE(toGreenToggle->value() == false); |
| |
| stateMachineInstance->pointerDown(rive::Vec2D(100.0f, 250.0f)); |
| // "gray-activate" is clicked |
| REQUIRE(grayToggle->value() == false); |
| // Pointer over "green-activate" |
| REQUIRE(toGreenToggle->value() == true); |
| |
| stateMachineInstance->pointerDown(rive::Vec2D(100.0f, 110.0f)); |
| // "gray-activate" is clicked |
| REQUIRE(grayToggle->value() == true); |
| // Pointer over "red-activate" and "green-activate", but "red-activate" is |
| // opaque and above so green activate does not trigger |
| REQUIRE(toGreenToggle->value() == false); |
| delete stateMachineInstance; |
| } |
| |
| TEST_CASE("hit test on opaque nested artboard", "[hittest]") |
| { |
| // This artboard (300x300) has a main rect at [0, 0, 300, 300] |
| // this rect has a listener that toggles "second-gray-toggle" |
| // and a nested artboard at [0, 0, 150, 150] |
| // the nested artboard and the rect have opaque targets |
| auto file = ReadRiveFile("assets/opaque_hit_test.riv"); |
| |
| auto artboard = file->artboard("second"); |
| auto artboardInstance = artboard->instance(); |
| auto stateMachine = artboard->stateMachine("second-state-machine"); |
| |
| REQUIRE(artboardInstance != nullptr); |
| REQUIRE(artboardInstance->stateMachineCount() == 1); |
| |
| REQUIRE(stateMachine != nullptr); |
| |
| rive::StateMachineInstance* stateMachineInstance = |
| new rive::StateMachineInstance(stateMachine, artboardInstance.get()); |
| |
| auto nestedArtboard = |
| stateMachineInstance->artboard()->find<rive::NestedArtboard>( |
| "second-nested"); |
| REQUIRE(nestedArtboard != nullptr); |
| auto nestedArtboardStateMachine = |
| nestedArtboard->nestedAnimations()[0]->as<NestedStateMachine>(); |
| REQUIRE(nestedArtboardStateMachine != nullptr); |
| auto nestedArtboardStateMachineInstance = |
| nestedArtboardStateMachine->stateMachineInstance(); |
| |
| auto secondNestedBoolTarget = |
| nestedArtboardStateMachineInstance->getBool("bool-target"); |
| REQUIRE(secondNestedBoolTarget != nullptr); |
| |
| artboardInstance->advance(0.0f); |
| stateMachineInstance->advanceAndApply(0.0f); |
| |
| REQUIRE(secondNestedBoolTarget->value() == false); |
| |
| auto secondGrayToggle = stateMachineInstance->getBool("second-gray-toggle"); |
| REQUIRE(secondGrayToggle != nullptr); |
| |
| stateMachineInstance->pointerDown(rive::Vec2D(100.0f, 250.0f)); |
| // toggle changes value because it is not under an opaque nested artboard |
| REQUIRE(secondGrayToggle->value() == true); |
| |
| stateMachineInstance->pointerDown(rive::Vec2D(301.0f, 50.0f)); |
| // toggle does not change because it is beyond the area of the square by 1 |
| // pixel And the 2px padding is unly used after the coarse grained test |
| // passes |
| REQUIRE(secondGrayToggle->value() == true); |
| |
| stateMachineInstance->pointerDown(rive::Vec2D(100.0f, 50.0f)); |
| // toggle does not change because it is under an opaque nested artboard |
| REQUIRE(secondGrayToggle->value() == true); |
| |
| // nested toggle changes because it's on top of shape |
| REQUIRE(secondNestedBoolTarget->value() == true); |
| |
| // A timeline switches draw order and the nested artboard is now below the |
| // rect |
| stateMachineInstance->advanceAndApply(1.0f); |
| stateMachineInstance->advance(0.0f); |
| |
| stateMachineInstance->pointerDown(rive::Vec2D(100.0f, 50.0f)); |
| // So now the pointer down is captured by the rect |
| REQUIRE(secondGrayToggle->value() == false); |
| |
| // nested toggle does not change because it's below shape |
| REQUIRE(secondNestedBoolTarget->value() == true); |
| delete stateMachineInstance; |
| } |
| |
| TEST_CASE("early out on listeners", "[hittest]") |
| { |
| auto file = ReadRiveFile("assets/pointer_events.riv"); |
| |
| auto artboard = file->artboard("art-1"); |
| auto artboardInstance = artboard->instance(); |
| auto stateMachine = artboard->stateMachine("sm-1"); |
| |
| REQUIRE(artboardInstance != nullptr); |
| REQUIRE(artboardInstance->stateMachineCount() == 1); |
| |
| REQUIRE(stateMachine != nullptr); |
| |
| rive::StateMachineInstance* stateMachineInstance = |
| new rive::StateMachineInstance(stateMachine, artboardInstance.get()); |
| |
| stateMachineInstance->advance(0.0f); |
| artboardInstance->advance(0.0f); |
| REQUIRE(stateMachineInstance->needsAdvance() == true); |
| stateMachineInstance->advance(0.0f); |
| REQUIRE(stateMachineInstance->hitComponentsCount() == 4); |
| // Hit component with only pointer down and pointer up listeners |
| auto hitComponentWithEarlyOut = stateMachineInstance->hitComponent(0); |
| // Hit component that can't early out because it has a pointer enter event |
| auto hitComponentWithNoEarlyOut = stateMachineInstance->hitComponent(1); |
| // Hit component that can't early out because it is an opaque target |
| auto hitComponentOpaque = stateMachineInstance->hitComponent(2); |
| // Hit component that can early out on all and pointer up |
| auto hitComponentOnlyPointerDown = stateMachineInstance->hitComponent(3); |
| REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 0); |
| REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); |
| REQUIRE(hitComponentOpaque->earlyOutCount == 0); |
| REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 0); |
| stateMachineInstance->pointerMove(rive::Vec2D(100.0f, 250.0f)); |
| REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 1); |
| REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); |
| REQUIRE(hitComponentOpaque->earlyOutCount == 0); |
| REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 1); |
| stateMachineInstance->pointerExit(rive::Vec2D(100.0f, 250.0f)); |
| REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 2); |
| REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); |
| REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 2); |
| REQUIRE(hitComponentOpaque->earlyOutCount == 0); |
| stateMachineInstance->pointerDown(rive::Vec2D(100.0f, 250.0f)); |
| REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 2); |
| REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); |
| REQUIRE(hitComponentOpaque->earlyOutCount == 0); |
| REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 2); |
| stateMachineInstance->pointerUp(rive::Vec2D(100.0f, 250.0f)); |
| REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 2); |
| REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); |
| REQUIRE(hitComponentOpaque->earlyOutCount == 0); |
| REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 3); |
| stateMachineInstance->pointerMove(rive::Vec2D(105.0f, 205.0f)); |
| REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 3); |
| REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); |
| REQUIRE(hitComponentOpaque->earlyOutCount == 0); |
| REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 4); |
| |
| delete stateMachineInstance; |
| } |
| |
| TEST_CASE("click event", "[hittest]") |
| { |
| // This test has two rectangles of size [200, 200] |
| // positioned at [100,100] and [200, 200] |
| // they overlap between coordinates [100,100]-[200, 200] |
| // they are inside a group that has a listener attached to it |
| // that listener should fire an event on "Click" |
| auto file = ReadRiveFile("assets/click_event.riv"); |
| |
| auto artboard = file->artboard("art-1"); |
| auto artboardInstance = artboard->instance(); |
| auto stateMachine = artboard->stateMachine("sm-1"); |
| |
| REQUIRE(artboardInstance != nullptr); |
| REQUIRE(artboardInstance->stateMachineCount() == 1); |
| |
| REQUIRE(stateMachine != nullptr); |
| |
| rive::StateMachineInstance* stateMachineInstance = |
| new rive::StateMachineInstance(stateMachine, artboardInstance.get()); |
| |
| stateMachineInstance->advance(0.0f); |
| artboardInstance->advance(0.0f); |
| REQUIRE(stateMachineInstance->needsAdvance() == true); |
| stateMachineInstance->advance(0.0f); |
| // There is a single listener with two shapes in it |
| REQUIRE(stateMachineInstance->hitComponentsCount() == 2); |
| auto layerCount = stateMachine->layerCount(); |
| REQUIRE(layerCount == 1); |
| REQUIRE(stateMachineInstance->reportedEventCount() == 0); |
| // Click in place should trigger a click event |
| stateMachineInstance->pointerDown(rive::Vec2D(75.0f, 75.0f)); |
| stateMachineInstance->pointerUp(rive::Vec2D(75.0f, 75.0f)); |
| REQUIRE(stateMachineInstance->reportedEventCount() == 1); |
| // Pointer down inside shape but Pointer up outside the shape |
| // should not trigger a click event |
| stateMachineInstance->pointerDown(rive::Vec2D(75.0f, 75.0f)); |
| stateMachineInstance->pointerUp(rive::Vec2D(300.0f, 75.0f)); |
| REQUIRE(stateMachineInstance->reportedEventCount() == 1); |
| // Pointer down outside shape but Pointer up inside the shape |
| // should not trigger a click event |
| stateMachineInstance->pointerDown(rive::Vec2D(300.0f, 75.0f)); |
| stateMachineInstance->pointerUp(rive::Vec2D(75.0f, 75.0f)); |
| REQUIRE(stateMachineInstance->reportedEventCount() == 1); |
| // Pointer down in shape 1 Pointer up in shape 2 of the same group |
| // should trigger a click event |
| stateMachineInstance->pointerDown(rive::Vec2D(75.0f, 75.0f)); |
| stateMachineInstance->pointerUp(rive::Vec2D(225.0f, 225.0f)); |
| REQUIRE(stateMachineInstance->reportedEventCount() == 2); |
| // Pointer down and up in area where both shapes overlap |
| // should trigger a single click event |
| stateMachineInstance->pointerDown(rive::Vec2D(150.0f, 150.0f)); |
| stateMachineInstance->pointerUp(rive::Vec2D(150.0f, 150.0f)); |
| REQUIRE(stateMachineInstance->reportedEventCount() == 3); |
| |
| delete stateMachineInstance; |
| } |
| |
| TEST_CASE("multiple shapes with mouse movement behavior", "[hittest]") |
| { |
| // This test has two rectangles of size [200, 200] |
| // positioned at [100,100] and [100, 200] |
| // they overlap between coordinates [100,0]-[200, 200] |
| // they are inside a group that has a Pointer enter and a Pointer out |
| // listeners that toggle between two states (red and green) |
| // starting at "red" |
| auto file = ReadRiveFile("assets/click_event.riv"); |
| |
| auto artboard = file->artboard("art-2"); |
| auto artboardInstance = artboard->instance(); |
| auto stateMachine = artboard->stateMachine("sm-1"); |
| |
| REQUIRE(artboardInstance != nullptr); |
| REQUIRE(artboardInstance->stateMachineCount() == 1); |
| |
| REQUIRE(stateMachine != nullptr); |
| |
| rive::StateMachineInstance* stateMachineInstance = |
| new rive::StateMachineInstance(stateMachine, artboardInstance.get()); |
| |
| stateMachineInstance->advance(0.0f); |
| artboardInstance->advance(0.0f); |
| REQUIRE(stateMachineInstance->needsAdvance() == true); |
| stateMachineInstance->advance(0.0f); |
| // There is a single listener with two shapes in it |
| REQUIRE(stateMachineInstance->hitComponentsCount() == 2); |
| auto layerCount = stateMachine->layerCount(); |
| REQUIRE(layerCount == 1); |
| // Move over the first shape |
| stateMachineInstance->pointerMove(rive::Vec2D(75.0f, 75.0f)); |
| artboardInstance->advance(0.0f); |
| stateMachineInstance->advanceAndApply(0.0f); |
| |
| { |
| auto state = stateMachineInstance->layerState(0); |
| REQUIRE(state->is<rive::AnimationState>()); |
| auto animation = state->as<rive::AnimationState>()->animation(); |
| REQUIRE(animation->name() == "green"); |
| } |
| // Move over the second shape, nothing should change |
| stateMachineInstance->pointerMove(rive::Vec2D(200.0f, 75.0f)); |
| artboardInstance->advance(0.0f); |
| stateMachineInstance->advanceAndApply(0.0f); |
| |
| { |
| auto state = stateMachineInstance->layerState(0); |
| REQUIRE(state->is<rive::AnimationState>()); |
| auto animation = state->as<rive::AnimationState>()->animation(); |
| REQUIRE(animation->name() == "green"); |
| } |
| // Move out of the second shape, should go back to red |
| stateMachineInstance->pointerMove(rive::Vec2D(400.0f, 75.0f)); |
| artboardInstance->advance(0.0f); |
| stateMachineInstance->advanceAndApply(0.0f); |
| |
| { |
| auto state = stateMachineInstance->layerState(0); |
| REQUIRE(state->is<rive::AnimationState>()); |
| auto animation = state->as<rive::AnimationState>()->animation(); |
| REQUIRE(animation->name() == "red"); |
| } |
| // Move back into the second shape, should go to green |
| stateMachineInstance->pointerMove(rive::Vec2D(200.0f, 75.0f)); |
| artboardInstance->advance(0.0f); |
| stateMachineInstance->advanceAndApply(0.0f); |
| |
| { |
| auto state = stateMachineInstance->layerState(0); |
| REQUIRE(state->is<rive::AnimationState>()); |
| auto animation = state->as<rive::AnimationState>()->animation(); |
| REQUIRE(animation->name() == "green"); |
| } |
| |
| delete stateMachineInstance; |
| } |
| |
| TEST_CASE("Shape clipped by parent layout", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/hit_test_test.riv", &silver); |
| |
| auto artboard = file->artboardNamed("ab-1"); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| REQUIRE(artboard != nullptr); |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| // Move over the shape |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(50.0f, 150.0f)); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| |
| // Move within the shape but the wrapping layout is clipped |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(260.0f, 150.0f)); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| |
| CHECK(silver.matches("hittest_ab1")); |
| } |
| |
| TEST_CASE("Shape clipped by parent artboard", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/hit_test_test.riv", &silver); |
| |
| auto artboard = file->artboardNamed("ab1-parent"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| // Move over the shape |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(370.0f, 110.0f)); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| |
| // Move within the shape but the wrapping parent layout in the nested |
| // artboard is clipped |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(370.0f, 180.0f)); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| |
| CHECK(silver.matches("hittest_ab1_parent")); |
| } |
| |
| TEST_CASE("Shape clipped by parent and grand-parent artboard", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/hit_test_test.riv", &silver); |
| |
| auto artboard = file->artboardNamed("ab1-grand-parent"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| // Move over the shape but outside the grand parent clipping area |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(370.0f, 250.0f)); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| |
| // Move within the shape in a non-clipped area |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(370.0f, 190.0f)); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| |
| // Move over the shape but outside the parent clipping area |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(510.0f, 190.0f)); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| |
| CHECK(silver.matches("hittest_ab1_grand_parent")); |
| } |
| |
| TEST_CASE("Artboard list component with scrolling behavior", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/hit_test_test.riv", &silver); |
| |
| auto artboard = file->artboardNamed("ab-2-non-virtualized"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| auto scrollProp = |
| vmi->propertyValue("scroll-offset")->as<ViewModelInstanceNumber>(); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| silver.addFrame(); |
| scrollProp->propertyValue(-100); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| |
| auto initCoord = 0.0f; |
| |
| initCoord = 200.0f; |
| // First move in an area of the artboard where there are listed components |
| // but they are clipped. |
| while (initCoord > 100.0f) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(50.0f, initCoord)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| initCoord -= 10; |
| } |
| |
| // Next jump to a position of the state to start scrolling the elements |
| // Should be noticed that the previous hovered components did not turn green |
| initCoord = 75.0f; |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(50.0f, initCoord)); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| while (initCoord > -500.0f) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(50.0f, initCoord)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| initCoord -= 20; |
| } |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(50.0f, initCoord)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| // After scroll has ended, move pointer over all visible elements that |
| // should turn green |
| initCoord = 110.0f; |
| while (initCoord > -5.0f) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(50.0f, initCoord)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| initCoord -= 4; |
| } |
| |
| CHECK(silver.matches("hittest_ab_2_non_virtualized")); |
| } |
| |
| TEST_CASE( |
| "Artboard list component with scrolling behavior virtualized and carousel", |
| "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/hit_test_test.riv", &silver); |
| |
| auto artboard = file->artboardNamed("ab-2-virtualized"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| auto scrollProp = |
| vmi->propertyValue("scroll-offset")->as<ViewModelInstanceNumber>(); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| silver.addFrame(); |
| scrollProp->propertyValue(-100); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| |
| auto initCoord = 0.0f; |
| |
| initCoord = 200.0f; |
| // First move in an area of the artboard where there are listed components |
| // but they are clipped In this test, since the scroll is virtualized, the |
| // pointer is not actually hovering over clipped components at all |
| while (initCoord > 100.0f) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(50.0f, initCoord)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| initCoord -= 10; |
| } |
| |
| // Next jump to a position of the state to start scrolling the elements |
| // Should be noticed that the previous hovered components did not turn green |
| initCoord = 75.0f; |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(50.0f, initCoord)); |
| stateMachine->advanceAndApply(0.1f); |
| artboard->draw(renderer.get()); |
| while (initCoord > -500.0f) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(50.0f, initCoord)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| initCoord -= 20; |
| } |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(50.0f, initCoord)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| // After scroll has ended, move pointer over all visible elements that |
| // should turn green |
| initCoord = 110.0f; |
| while (initCoord > -5.0f) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(50.0f, initCoord)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| initCoord -= 4; |
| } |
| |
| CHECK(silver.matches("hittest_ab_2_virtualized")); |
| } |
| |
| TEST_CASE("Hit testing text in multiple layouts rotated and scaled", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/hit_test_test.riv", &silver); |
| |
| auto artboard = file->artboardNamed("ab-text-parent"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| auto initCoord = 400.0f; |
| // First move the cursor from left to right through the text |
| // within a clipped and non-clipped area |
| while (initCoord < 550.0f) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(initCoord, 320.0f)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| initCoord += 10; |
| } |
| |
| initCoord = 200.0f; |
| // First move the cursor from top to bottom through the text |
| // within a clipped and non-clipped area |
| while (initCoord < 450.0f) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(500.0f, initCoord)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| initCoord += 10; |
| } |
| |
| CHECK(silver.matches("hittest_ab_text_parent")); |
| } |
| |
| TEST_CASE("Hit testing shapes in layouts", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/hit_test_test.riv", &silver); |
| |
| auto artboard = file->artboardNamed("ab-shape-parent"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| auto initCoord = 0.0f; |
| // First move the cursor from left to right through the text |
| // within a clipped and non-clipped area |
| while (initCoord < 550.0f) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(310.0f, initCoord)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| initCoord += 20; |
| } |
| |
| initCoord = 220.0f; |
| // First move the cursor from top to bottom through the text |
| // within a clipped and non-clipped area |
| while (initCoord < 530.0f) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(initCoord, 420.0f)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| initCoord += 20; |
| } |
| |
| CHECK(silver.matches("hittest_ab_shape_parent")); |
| } |
| |
| TEST_CASE("Hit testing objects inside shapes", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/hit_test_nested.riv", &silver); |
| |
| auto artboard = file->artboardNamed("Main"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| // Hover shape in another shape with no path |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(150.0f, 150.0f)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| // Hover nested artboard in another shape with no path |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(300.0f, 200.0f)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| // Hover text in another shape with path |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(100.0f, 250.0f)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| // Hover nested artboard in another shape with path |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(400.0f, 350.0f)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| CHECK(silver.matches("hittest_nested")); |
| } |
| TEST_CASE("Pointer exit works correctly", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/pointer_exit.riv", &silver); |
| |
| auto artboard = file->artboardNamed("main"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| // Move from [100.0, 250.0] to [400.0, 250.0] |
| // This will hover over two nested artboards and should unhover once an |
| // opaque target is hit |
| float mousePos = 100.0f; |
| for (mousePos = 100.0f; mousePos <= 400; mousePos += 30) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(mousePos, 250.0f)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| } |
| |
| // Move from [500.0, 250.0] to [100.0, 250.0] |
| // This movement will start at the opaque target and should only trigger the |
| // hover effect once it reaches the emousePosed sections of the nseted |
| // artboards |
| for (mousePos = 500.0f; mousePos > 100; mousePos -= 30) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(mousePos, 250.0f)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| } |
| |
| // Move from [240.0, 390.0] to [240.0, 90.0] |
| // This movement will start at the opaque target and should only trigger the |
| // hover effect once it reaches the emousePosed sections of the nseted |
| // artboards |
| for (mousePos = 500.0f; mousePos > 100; mousePos -= 30) |
| { |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(240.0f, mousePos)); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| } |
| |
| CHECK(silver.matches("pointer_exit")); |
| } |
| |
| TEST_CASE("Hit testing multi touch events", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/multitouch.riv", &silver); |
| |
| auto artboard = file->artboardNamed("main"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| // Simple click with single pointer |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(200.0f, 350.0f), 1); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(200.0f, 350.0f), 1); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| // New click gesture started with pointer id 1 |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(200.0f, 350.0f), 1); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| // Pointer up with pointer id 0 should not complete the click gesture |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(200.0f, 350.0f), 0); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| // Pointer up with pointer id 1 should complete the click gesture |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(200.0f, 350.0f), 1); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| // Two click gestures interleaved: 1 down - 0 down - 0 up - 1 up |
| // should toggle color twice |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(200.0f, 350.0f), 1); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(200.0f, 350.0f), 0); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(200.0f, 350.0f), 0); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(200.0f, 350.0f), 1); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| CHECK(silver.matches("multitouch")); |
| } |
| |
| TEST_CASE("Multitouch with nested artboard and pointer exit event", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/multitouch_enter.riv", &silver); |
| |
| auto artboard = file->artboardNamed("Main"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| // Script |
| // Advancing by 0.0000s |
| // Advancing by 0.0167s |
| // Touch (id: 9) began at {122.5845,443.8406} |
| // Advancing by 0.0167s |
| // Touch (id: 8) began at {459.5410,188.4058} |
| // Touch (id: 7) began at {333.3333,248.1884} |
| // Advancing by 0.0167s |
| // Touch (id: 8) ended at {459.5410,188.4058} |
| // Touch (id: 8) exited at {459.5410,188.4058} |
| // Touch (id: 9) ended at {123.7923,444.4445} |
| // Touch (id: 9) exited at {123.7923,444.4445} |
| // Touch (id: 7) ended at {333.3333,248.1884} |
| // Touch (id: 7) exited at {333.3333,248.1884} |
| // Advancing by 0.0167s |
| // Touch (id: 7) began at {118.9613,439.6135} |
| // Touch (id: 9) began at {346.6183,269.9276} |
| // Touch (id: 8) began at {459.5410,194.4444} |
| // Advancing by 0.0167s |
| // Touch (id: 9) ended at {346.6183,269.9276} |
| // Touch (id: 9) exited at {346.6183,269.9276} |
| // Touch (id: 7) ended at {122.5845,440.8212} |
| // Touch (id: 7) exited at {122.5845,440.8212} |
| // Touch (id: 8) ended at {459.5410,194.4444} |
| // Touch (id: 8) exited at {459.5410,194.4444} |
| // Advancing by 0.0167s |
| |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(122.5845f, 443.8406f), 9); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(459.5410f, 188.4058f), 8); |
| stateMachine->pointerDown(rive::Vec2D(333.3333f, 248.1884f), 7); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(459.5410f, 188.4058f), 8); |
| stateMachine->pointerExit(rive::Vec2D(459.5410f, 188.4058f), 8); |
| stateMachine->pointerUp(rive::Vec2D(123.7923f, 444.4445f), 9); |
| stateMachine->pointerExit(rive::Vec2D(123.7923f, 444.4445f), 9); |
| stateMachine->pointerUp(rive::Vec2D(333.3333f, 248.1884f), 7); |
| stateMachine->pointerExit(rive::Vec2D(333.3333f, 248.1884f), 7); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(118.9613f, 439.6135f), 7); |
| stateMachine->pointerDown(rive::Vec2D(346.6183f, 269.9276f), 9); |
| stateMachine->pointerDown(rive::Vec2D(459.5410f, 194.4444f), 8); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(346.6183f, 269.9276f), 9); |
| stateMachine->pointerExit(rive::Vec2D(346.6183f, 269.9276f), 9); |
| stateMachine->pointerUp(rive::Vec2D(122.5845f, 440.8212f), 7); |
| stateMachine->pointerExit(rive::Vec2D(122.5845f, 440.8212f), 7); |
| stateMachine->pointerUp(rive::Vec2D(459.5410f, 194.4444f), 8); |
| stateMachine->pointerExit(rive::Vec2D(459.5410f, 194.4444f), 8); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| |
| CHECK(silver.matches("multitouch_enter")); |
| } |
| |
| TEST_CASE("Multitouch with list and pointer exit event", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/multitouch_enter.riv", &silver); |
| |
| auto artboard = file->artboardNamed("MainList"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| int viewModelId = artboard.get()->viewModelId(); |
| |
| auto vmi = viewModelId == -1 |
| ? file->createViewModelInstance(artboard.get()) |
| : file->createViewModelInstance(viewModelId, 0); |
| |
| stateMachine->bindViewModelInstance(vmi); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(122.5845f, 443.8406f), 9); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(459.5410f, 188.4058f), 8); |
| stateMachine->pointerDown(rive::Vec2D(333.3333f, 248.1884f), 7); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(459.5410f, 188.4058f), 8); |
| stateMachine->pointerExit(rive::Vec2D(459.5410f, 188.4058f), 8); |
| stateMachine->pointerUp(rive::Vec2D(123.7923f, 444.4445f), 9); |
| stateMachine->pointerExit(rive::Vec2D(123.7923f, 444.4445f), 9); |
| stateMachine->pointerUp(rive::Vec2D(333.3333f, 248.1884f), 7); |
| stateMachine->pointerExit(rive::Vec2D(333.3333f, 248.1884f), 7); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| silver.addFrame(); |
| stateMachine->pointerDown(rive::Vec2D(118.9613f, 439.6135f), 7); |
| stateMachine->pointerDown(rive::Vec2D(346.6183f, 269.9276f), 9); |
| stateMachine->pointerDown(rive::Vec2D(459.5410f, 194.4444f), 8); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| silver.addFrame(); |
| stateMachine->pointerUp(rive::Vec2D(346.6183f, 269.9276f), 9); |
| stateMachine->pointerExit(rive::Vec2D(346.6183f, 269.9276f), 9); |
| stateMachine->pointerUp(rive::Vec2D(122.5845f, 440.8212f), 7); |
| stateMachine->pointerExit(rive::Vec2D(122.5845f, 440.8212f), 7); |
| stateMachine->pointerUp(rive::Vec2D(459.5410f, 194.4444f), 8); |
| stateMachine->pointerExit(rive::Vec2D(459.5410f, 194.4444f), 8); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(50.0f, 300.0f), 0, 7); |
| stateMachine->pointerMove(rive::Vec2D(250.0f, 200.0f), 0, 8); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| float xOffset = 0; |
| while (xOffset < 300) |
| { |
| xOffset += 20; |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(50.0f + xOffset, 300.0f), 0, 7); |
| stateMachine->pointerMove(rive::Vec2D(250.0f + xOffset, 200.0f), 0, 8); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| } |
| |
| CHECK(silver.matches("multitouch_enter-MainList")); |
| } |
| |
| TEST_CASE("Multitouch with multi scroll", "[silver]") |
| { |
| SerializingFactory silver; |
| auto file = ReadRiveFile("assets/multitouch_enter.riv", &silver); |
| |
| auto artboard = file->artboardNamed("MultiScroll"); |
| REQUIRE(artboard != nullptr); |
| |
| silver.frameSize(artboard->width(), artboard->height()); |
| |
| auto stateMachine = artboard->stateMachineAt(0); |
| stateMachine->advanceAndApply(0.1f); |
| |
| auto renderer = silver.makeRenderer(); |
| artboard->draw(renderer.get()); |
| |
| silver.addFrame(); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| float yOffset = 400; |
| stateMachine->pointerDown(rive::Vec2D(50.0f, yOffset), 7); |
| stateMachine->pointerDown(rive::Vec2D(350.0f, yOffset), 8); |
| while (yOffset > 0) |
| { |
| yOffset -= 20; |
| silver.addFrame(); |
| stateMachine->pointerMove(rive::Vec2D(50.0f, yOffset), 0, 7); |
| stateMachine->pointerMove(rive::Vec2D(350.0f, yOffset), 0, 8); |
| stateMachine->advanceAndApply(0.016f); |
| artboard->draw(renderer.get()); |
| } |
| stateMachine->pointerUp(rive::Vec2D(50.0f, yOffset), 7); |
| stateMachine->pointerUp(rive::Vec2D(350.0f, yOffset), 8); |
| |
| CHECK(silver.matches("multitouch_enter-MultiScroll")); |
| } |