| #include "rive/file.hpp" | |
| #include "rive/animation/linear_animation.hpp" | |
| #include "rive/animation/linear_animation_instance.hpp" | |
| #include "rive/animation/state_machine_instance.hpp" | |
| #include "rive/viewmodel/viewmodel.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_boolean.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_color.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_enum.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_list.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_number.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_trigger.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_string.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" | |
| #include "rive/viewmodel/runtime/viewmodel_runtime.hpp" | |
| #include "rive/math/random.hpp" | |
| // #include "rive/input/gamepad_batch.hpp" | |
| #include "utils/serializing_factory.hpp" | |
| #include "rive_file_reader.hpp" | |
| #include <catch.hpp> | |
| #include <cstdio> | |
| #include <cstring> | |
| #include "rive/profiler/profiler_macros.h" | |
| #include <filesystem> | |
| #include <fstream> | |
| #include <iostream> | |
| using namespace rive; | |
| TEST_CASE("juice silver", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/juice.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| silver.frameSize(artboard->width(), artboard->height()); | |
| REQUIRE(artboard != nullptr); | |
| auto walkAnimation = artboard->animationNamed("walk"); | |
| REQUIRE(walkAnimation != nullptr); | |
| auto renderer = silver.makeRenderer(); | |
| // Draw first frame. | |
| walkAnimation->advanceAndApply(0.0f); | |
| artboard->draw(renderer.get()); | |
| int frames = (int)(walkAnimation->durationSeconds() / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| walkAnimation->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("juice")); | |
| } | |
| TEST_CASE("hide silver", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/hide_test.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| silver.frameSize(artboard->width(), artboard->height()); | |
| REQUIRE(artboard != nullptr); | |
| auto stateMachine = artboard->stateMachineAt(0); | |
| auto vmi = file->createViewModelInstance(1, 0); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("hide_test")); | |
| } | |
| TEST_CASE("n-slice silver", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/n_slice_triangle.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| silver.frameSize(artboard->width(), artboard->height()); | |
| artboard->advance(0.0f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("n_slice_triangle")); | |
| } | |
| TEST_CASE("lock icon listener silver", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/lock_icon_demo.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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(); | |
| // Click in the middle of the state machine. | |
| stateMachine->pointerDown( | |
| rive::Vec2D(artboard->width() / 2.0f, artboard->height() / 2.0f)); | |
| // Advance and apply twice to take the transition and apply the next state. | |
| stateMachine->advanceAndApply(0.1f); | |
| stateMachine->advanceAndApply(1.0f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| // Do it again to lock the icon. | |
| stateMachine->pointerDown( | |
| rive::Vec2D(artboard->width() / 2.0f, artboard->height() / 2.0f)); | |
| // Advance and apply twice to take the transition and apply the next state. | |
| stateMachine->advanceAndApply(0.1f); | |
| stateMachine->advanceAndApply(1.0f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("lock_icon_demo")); | |
| } | |
| TEST_CASE("validate text run listener works", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/text_listener_simpler.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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(); | |
| // Click in the middle of the state machine. | |
| stateMachine->pointerDown( | |
| rive::Vec2D(artboard->width() * 0.8, artboard->height() / 2.0f)); | |
| // Advance and apply twice to take the transition and apply the next state. | |
| stateMachine->advanceAndApply(0.1f); | |
| stateMachine->advanceAndApply(1.0f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("text_listener_simpler")); | |
| } | |
| TEST_CASE("validate text with modifiers and dashes render correctly", | |
| "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/text_stroke_test.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| silver.frameSize(artboard->width(), artboard->height()); | |
| auto stateMachine = artboard->stateMachineAt(0); | |
| stateMachine->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| // Draw first frame. | |
| artboard->draw(renderer.get()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("text_stroke_test")); | |
| } | |
| TEST_CASE("superbowl data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| File::deterministicMode = true; | |
| auto file = ReadRiveFile("assets/superbowl.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| int frames = (int)(10.0f / 1.0f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(1.0f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("superbowl")); | |
| File::deterministicMode = false; | |
| } | |
| TEST_CASE("data viz demo data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/data_viz_demo.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| auto vmiItem1 = vmi->propertyValue("item1"); | |
| auto vmiItem1ViewModelInstance = vmiItem1->as<ViewModelInstanceViewModel>() | |
| ->referenceViewModelInstance() | |
| .get(); | |
| if (vmiItem1ViewModelInstance != nullptr) | |
| { | |
| auto valueProp = vmiItem1ViewModelInstance->propertyValue("value"); | |
| if (valueProp != nullptr) | |
| { | |
| valueProp->as<ViewModelInstanceNumber>()->propertyValue(20); | |
| } | |
| } | |
| int frames = (int)(0.8f / 0.064f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.064f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("data_viz_demo")); | |
| } | |
| TEST_CASE("bank card data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/bankcard.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| int frames = (int)(2.0f / 0.1f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("bankcard")); | |
| } | |
| TEST_CASE("ai assistant data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/ai_assitant.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| auto leftNumber = vmi->propertyValue("left"); | |
| auto bottomNumber = vmi->propertyValue("bottom"); | |
| auto topNumber = vmi->propertyValue("top"); | |
| auto rightNumber = vmi->propertyValue("right"); | |
| int frames = (int)(1.0f / 0.33f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| leftNumber->as<ViewModelInstanceNumber>()->propertyValue(i * 10); | |
| bottomNumber->as<ViewModelInstanceNumber>()->propertyValue(i * 5); | |
| topNumber->as<ViewModelInstanceNumber>()->propertyValue(i * 3); | |
| rightNumber->as<ViewModelInstanceNumber>()->propertyValue(i * 2); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("ai_assitant")); | |
| } | |
| TEST_CASE("echo show demo data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/echo_show_demo.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("echo_show_demo")); | |
| } | |
| TEST_CASE("rewards demo data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/rewards_demo.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| auto buttonProp = vmi->propertyValue("Button"); | |
| if (buttonProp != nullptr) | |
| { | |
| auto buttonVm = buttonProp->as<ViewModelInstanceViewModel>() | |
| ->referenceViewModelInstance() | |
| .get(); | |
| if (buttonVm != nullptr) | |
| { | |
| auto trigger = buttonVm->propertyValue("Pressed"); | |
| trigger->as<ViewModelInstanceTrigger>()->trigger(); | |
| } | |
| } | |
| int frames = (int)(5.0f / 0.25f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("rewards_demo")); | |
| } | |
| TEST_CASE("spotify kids demo data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/spotify_kids_demo.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("spotify_kids_demo")); | |
| } | |
| TEST_CASE("spotify kids app icon data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/spotify_kids_app_icon.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("spotify_kids_app_icon")); | |
| } | |
| TEST_CASE("hunter x demo data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/hunter_x_demo.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("hunter_x_demo")); | |
| } | |
| TEST_CASE("health tracker data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/db_health_tracker.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("db_health_tracker")); | |
| } | |
| TEST_CASE("car widgets data binding", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/car_widgets_v01.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| auto vmiItem1 = vmi->propertyValue("COMPASS"); | |
| auto vmiItem1ViewModelInstance = vmiItem1->as<ViewModelInstanceViewModel>() | |
| ->referenceViewModelInstance() | |
| .get(); | |
| if (vmiItem1ViewModelInstance != nullptr) | |
| { | |
| auto valueProp = vmiItem1ViewModelInstance->propertyValue("Rotation"); | |
| if (valueProp != nullptr) | |
| { | |
| valueProp->as<ViewModelInstanceNumber>()->propertyValue(20); | |
| } | |
| } | |
| auto vmiItem2 = vmi->propertyValue("TIRE PSI"); | |
| auto vmiItem2ViewModelInstance = vmiItem2->as<ViewModelInstanceViewModel>() | |
| ->referenceViewModelInstance() | |
| .get(); | |
| if (vmiItem2ViewModelInstance != nullptr) | |
| { | |
| auto valueProp = vmiItem2ViewModelInstance->propertyValue("FL Tyre"); | |
| if (valueProp != nullptr) | |
| { | |
| valueProp->as<ViewModelInstanceNumber>()->propertyValue(10); | |
| } | |
| } | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("car_widgets_v01")); | |
| } | |
| TEST_CASE("Vertical align on text with ellipsis", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/vertical_align_ellipsis.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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()); | |
| CHECK(silver.matches("vertical_align_ellipsis")); | |
| } | |
| TEST_CASE("Event triggers another event", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/event_trigger_event.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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(); | |
| // Click in a square that fires a trigger. This trigger will cause a | |
| // transition that fires an event. There is a listener on that event that | |
| // fires a second event. | |
| stateMachine->pointerDown(rive::Vec2D(475, 25)); | |
| stateMachine->pointerUp(rive::Vec2D(475, 25)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("event_trigger_event")); | |
| } | |
| TEST_CASE("Collapsed data binds from layouts with display hidden", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/collapse_data_binds.riv", &silver); | |
| auto artboard = file->artboardNamed("test-1"); | |
| 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->advanceAndApply(0.0f); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.016f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("collapse_data_binds-test_1")); | |
| } | |
| TEST_CASE("Collapsed data binds from property groups in solos", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/collapse_data_binds.riv", &silver); | |
| auto artboard = file->artboardNamed("test-2"); | |
| 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->advanceAndApply(0.0f); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.016f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("collapse_data_binds-test_2")); | |
| } | |
| TEST_CASE("Collapsed data bound layout styles still update", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/collapse_data_binds.riv", &silver); | |
| auto artboard = file->artboardNamed("test-3"); | |
| 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 display1 = | |
| vmi->propertyValue("display_1")->as<ViewModelInstanceEnum>(); | |
| auto display2 = | |
| vmi->propertyValue("display_2")->as<ViewModelInstanceEnum>(); | |
| stateMachine->advanceAndApply(0.0f); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.016f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| display2->value(1); // Hide inner layout | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| display1->value(1); // Hide outer layout | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| display2->value(0); // Show inner layout (nothing changes) | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| display1->value(0); // Show outer layout (nothing changes) | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("collapse_data_binds-test_3")); | |
| } | |
| TEST_CASE("Reset randomization on value change", "[silver]") | |
| { | |
| File::deterministicMode = true; | |
| RandomProvider::clearRandoms(); | |
| REQUIRE(RandomProvider::totalCalls() == 0); | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/formula_random.riv", &silver); | |
| auto artboard = file->artboardNamed("source_change"); | |
| 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 numProp = vmi->propertyValue("n1")->as<ViewModelInstanceNumber>(); | |
| RandomProvider::addRandomValue(0.0f); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| REQUIRE(RandomProvider::totalCalls() == 1); | |
| silver.addFrame(); | |
| RandomProvider::addRandomValue(1.0f); | |
| numProp->propertyValue(500); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| // Random generation hasn't been called again | |
| REQUIRE(RandomProvider::totalCalls() == 2); | |
| RandomProvider::clearRandoms(); | |
| CHECK(silver.matches("formula_random-source_change")); | |
| File::deterministicMode = false; | |
| } | |
| TEST_CASE("Reset randomization only once", "[silver]") | |
| { | |
| File::deterministicMode = true; | |
| RandomProvider::clearRandoms(); | |
| REQUIRE(RandomProvider::totalCalls() == 0); | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/formula_random.riv", &silver); | |
| auto artboard = file->artboardNamed("once"); | |
| 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 numProp = vmi->propertyValue("n1")->as<ViewModelInstanceNumber>(); | |
| RandomProvider::addRandomValue(0.0f); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| REQUIRE(RandomProvider::totalCalls() == 1); | |
| silver.addFrame(); | |
| RandomProvider::addRandomValue(1.0f); | |
| numProp->propertyValue(500); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| // Random generation has been called again | |
| REQUIRE(RandomProvider::totalCalls() == 1); | |
| RandomProvider::clearRandoms(); | |
| CHECK(silver.matches("formula_random-once")); | |
| File::deterministicMode = false; | |
| } | |
| TEST_CASE("Reset randomization on every change", "[silver]") | |
| { | |
| File::deterministicMode = true; | |
| RandomProvider::clearRandoms(); | |
| REQUIRE(RandomProvider::totalCalls() == 0); | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/formula_random.riv", &silver); | |
| auto artboard = file->artboardNamed("always"); | |
| 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 numProp = vmi->propertyValue("n1")->as<ViewModelInstanceNumber>(); | |
| RandomProvider::addRandomValue(0.0f); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| REQUIRE(RandomProvider::totalCalls() == 1); | |
| silver.addFrame(); | |
| numProp->propertyValue(500); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| // Random generation hasn't been called on every advance | |
| REQUIRE(RandomProvider::totalCalls() == 64); | |
| RandomProvider::clearRandoms(); | |
| CHECK(silver.matches("formula_random-always")); | |
| File::deterministicMode = false; | |
| } | |
| TEST_CASE("Target to source with different data types on source and target", | |
| "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/saturation.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->advanceAndApply(0.0f); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.016f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("saturation")); | |
| } | |
| TEST_CASE("interactive and non interactive scrolling", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/interactive_scrolling.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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); | |
| auto boolProp = | |
| vmi->propertyValue("isInteractive")->as<ViewModelInstanceBoolean>(); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| double yPos = artboard->height() - 20; | |
| silver.addFrame(); | |
| stateMachine->pointerDown(rive::Vec2D(artboard->width() / 2.0f, yPos)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| while (yPos > 120) | |
| { | |
| silver.addFrame(); | |
| stateMachine->pointerMove(rive::Vec2D(artboard->width() / 2.0f, yPos)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| yPos -= 20; | |
| } | |
| silver.addFrame(); | |
| stateMachine->pointerUp(rive::Vec2D(artboard->width() / 2.0f, yPos)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| // Change to interactive | |
| boolProp->propertyValue(true); | |
| stateMachine->advanceAndApply(0.1f); | |
| yPos = artboard->height() - 20; | |
| silver.addFrame(); | |
| stateMachine->pointerDown(rive::Vec2D(artboard->width() / 2.0f, yPos)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| while (yPos > 120) | |
| { | |
| silver.addFrame(); | |
| stateMachine->pointerMove(rive::Vec2D(artboard->width() / 2.0f, yPos)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| yPos -= 20; | |
| } | |
| silver.addFrame(); | |
| stateMachine->pointerUp(rive::Vec2D(artboard->width() / 2.0f, yPos)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("interactive_scrolling")); | |
| } | |
| TEST_CASE("Interpolator returns advance status as true until it settles", | |
| "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/interpolate_to_end.riv", &silver); | |
| auto artboard = file->artboardNamed("child"); | |
| 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); | |
| auto numProp = vmi->propertyValue("num")->as<ViewModelInstanceNumber>(); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| numProp->propertyValue(1000); | |
| stateMachine->advanceAndApply(0.001f); | |
| auto shouldAdvance = true; | |
| while (shouldAdvance) | |
| { | |
| silver.addFrame(); | |
| shouldAdvance = stateMachine->advanceAndApply(0.25f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("interpolate_to_end")); | |
| } | |
| TEST_CASE("View models keep reference to their dependents and parents", | |
| "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/replace_vm_instance.riv", &silver); | |
| auto artboardMain1 = file->artboardNamed("main-1"); | |
| auto artboardMain2 = file->artboardNamed("main-2"); | |
| auto artboardCommon = file->artboardNamed("common"); | |
| REQUIRE(artboardMain1 != nullptr); | |
| REQUIRE(artboardMain2 != nullptr); | |
| REQUIRE(artboardCommon != nullptr); | |
| silver.frameSize(artboardMain1->width(), artboardMain1->height()); | |
| std::vector<StateMachineInstance*> stateMachines; | |
| auto stateMachineMain1 = artboardMain1->stateMachineAt(0); | |
| auto stateMachineMain2 = artboardMain2->stateMachineAt(0); | |
| auto stateMachineCommon = artboardCommon->stateMachineAt(0); | |
| stateMachines.push_back(stateMachineMain1.get()); | |
| stateMachines.push_back(stateMachineMain2.get()); | |
| stateMachines.push_back(stateMachineCommon.get()); | |
| // Each view model instance has its own independent tree | |
| auto vmiMain1 = | |
| file->createViewModelInstance(artboardMain1.get()->viewModelId(), 0); | |
| auto vmiMain2 = | |
| file->createViewModelInstance(artboardMain2.get()->viewModelId(), 0); | |
| auto vmiCommon = | |
| file->createViewModelInstance(artboardCommon.get()->viewModelId(), 0); | |
| stateMachineMain1->bindViewModelInstance(vmiMain1); | |
| stateMachineMain1->advanceAndApply(0.1f); | |
| stateMachineMain2->bindViewModelInstance(vmiMain2); | |
| stateMachineMain2->advanceAndApply(0.1f); | |
| stateMachineCommon->bindViewModelInstance(vmiCommon); | |
| stateMachineCommon->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| artboardMain1->draw(renderer.get()); | |
| silver.addFrame(); | |
| artboardMain2->draw(renderer.get()); | |
| silver.addFrame(); | |
| artboardCommon->draw(renderer.get()); | |
| for (auto& sm : stateMachines) | |
| { | |
| silver.addFrame(); | |
| sm->advanceAndApply(0.1f); | |
| sm->artboard()->draw(renderer.get()); | |
| } | |
| // Changing the text value of one instance only affects that instance | |
| auto vmCommon1Prop = vmiMain1->propertyValue("common-1") | |
| ->as<ViewModelInstanceViewModel>() | |
| ->referenceViewModelInstance() | |
| .get(); | |
| auto vmChildProp = vmCommon1Prop->propertyValue("ch") | |
| ->as<ViewModelInstanceViewModel>() | |
| ->referenceViewModelInstance(); | |
| auto stringProp = | |
| vmChildProp.get()->propertyValue("str")->as<ViewModelInstanceString>(); | |
| stringProp->propertyValue("update-1"); | |
| for (auto& sm : stateMachines) | |
| { | |
| silver.addFrame(); | |
| sm->advanceAndApply(0.1f); | |
| sm->artboard()->draw(renderer.get()); | |
| } | |
| // Replace the nested child view model of main2 with the same instance of | |
| // main1 Instance 2 should update | |
| auto vmCommonMain2Prop = vmiMain2->propertyValue("common-2") | |
| ->as<ViewModelInstanceViewModel>() | |
| ->referenceViewModelInstance() | |
| .get(); | |
| vmCommonMain2Prop->replaceViewModelByName("ch", vmChildProp); | |
| for (auto& sm : stateMachines) | |
| { | |
| silver.addFrame(); | |
| sm->advanceAndApply(0.1f); | |
| sm->artboard()->draw(renderer.get()); | |
| } | |
| // Updating the value, changes it on both artboard | |
| stringProp->propertyValue("update-2"); | |
| for (auto& sm : stateMachines) | |
| { | |
| silver.addFrame(); | |
| sm->advanceAndApply(0.1f); | |
| sm->artboard()->draw(renderer.get()); | |
| } | |
| // Replace the child view model of common with the same instance of | |
| // main1 | |
| // common should update now too | |
| auto currentChild = | |
| vmiCommon->propertyValue("ch")->as<ViewModelInstanceViewModel>(); | |
| auto referenceViewModel = currentChild->referenceViewModelInstance(); | |
| REQUIRE(referenceViewModel->parents().size() == 1); | |
| vmiCommon->replaceViewModelByName("ch", vmChildProp); | |
| REQUIRE(referenceViewModel->parents().size() == 0); | |
| REQUIRE(vmiMain1->dependents().size() == 1); | |
| REQUIRE(vmiMain2->dependents().size() == 1); | |
| stateMachineMain2->bindViewModelInstance(vmiMain1); | |
| REQUIRE(vmiMain1->dependents().size() == 2); | |
| REQUIRE(vmiMain2->dependents().size() == 0); | |
| for (auto& sm : stateMachines) | |
| { | |
| silver.addFrame(); | |
| sm->advanceAndApply(0.1f); | |
| sm->artboard()->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("replace_vm_instance")); | |
| } | |
| TEST_CASE("Replace view model instance in list", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/replace_vm_instance.riv", &silver); | |
| auto artboard = file->artboardNamed("main-list"); | |
| silver.frameSize(artboard->width(), artboard->height()); | |
| auto renderer = silver.makeRenderer(); | |
| auto stateMachine = artboard->stateMachineAt(0); | |
| auto vmi = file->createViewModelInstance(artboard.get()->viewModelId(), 0); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| // Replace an instance of a list and expect the binding to be automatically | |
| // called | |
| silver.addFrame(); | |
| // First create a instance | |
| auto grandChildInstance = | |
| file->createViewModelInstance("main-list-grandchild"); | |
| auto labelProp = grandChildInstance->propertyValue("label") | |
| ->as<ViewModelInstanceString>(); | |
| labelProp->propertyValue("modified"); | |
| // Second retrieve the item from the list that will be replaced | |
| auto lis = vmi->propertyValue("lis")->as<ViewModelInstanceList>(); | |
| { | |
| auto listItem = lis->item(1); | |
| auto listItemViewModelInstance = listItem->viewModelInstance(); | |
| // Third replace the property "grandchild" with the new instance created | |
| listItemViewModelInstance->replaceViewModelByName("grandchild", | |
| grandChildInstance); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| } | |
| // Create a new child instance and append it to the list, then replace its | |
| // child | |
| { | |
| silver.addFrame(); | |
| // First create instance to be added and set its label value | |
| auto childInstance = file->createViewModelInstance("main-list-child"); | |
| auto childGrandChild = childInstance->propertyValue("grandchild") | |
| ->as<ViewModelInstanceViewModel>() | |
| ->referenceViewModelInstance() | |
| .get(); | |
| auto childGrandChildLabel = childGrandChild->propertyValue("label") | |
| ->as<ViewModelInstanceString>(); | |
| childGrandChildLabel->propertyValue("new label"); | |
| // Second append it to the list | |
| auto listItem = make_rcp<ViewModelInstanceListItem>(); | |
| listItem->viewModelInstance(childInstance); | |
| lis->addItem(listItem); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| // Replace the view model property and expect the instance to be updated | |
| silver.addFrame(); | |
| auto grandChildInstance = | |
| file->createViewModelInstance("main-list-grandchild"); | |
| auto labelProp = grandChildInstance->propertyValue("label") | |
| ->as<ViewModelInstanceString>(); | |
| labelProp->propertyValue("modified 2"); | |
| childInstance->replaceViewModelByName("grandchild", grandChildInstance); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("replace_vm_instance-list")); | |
| } | |
| TEST_CASE("Replace view model instance in nested list", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/replace_vm_instance.riv", &silver); | |
| auto artboard = file->artboardNamed("main-double-nest"); | |
| silver.frameSize(artboard->width(), artboard->height()); | |
| auto renderer = silver.makeRenderer(); | |
| auto stateMachine = artboard->stateMachineAt(0); | |
| auto vmi = file->createViewModelInstance(artboard.get()->viewModelId(), 0); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| // Replace an instance of a list and expect the binding to be automatically | |
| // called | |
| silver.addFrame(); | |
| // First create a instance | |
| auto grandChildInstance = | |
| file->createViewModelInstance("main-list-grandchild"); | |
| auto labelProp = grandChildInstance->propertyValue("label") | |
| ->as<ViewModelInstanceString>(); | |
| labelProp->propertyValue("modified"); | |
| auto mainList = vmi->propertyValue("lis")->as<ViewModelInstanceList>(); | |
| auto mainListChild = mainList->item(0); | |
| auto mainListChildInstance = mainListChild->viewModelInstance(); | |
| // Second retrieve the item from the list that will be replaced | |
| auto lis = mainListChildInstance->propertyValue("lis") | |
| ->as<ViewModelInstanceList>(); | |
| { | |
| auto listItem = lis->item(1); | |
| auto listItemViewModelInstance = listItem->viewModelInstance(); | |
| // Third replace the property "grandchild" with the new instance created | |
| listItemViewModelInstance->replaceViewModelByName("grandchild", | |
| grandChildInstance); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| } | |
| // Create a new child instance and append it to the list, then replace its | |
| // child | |
| { | |
| silver.addFrame(); | |
| // First create instance to be added and set its label value | |
| auto childInstance = file->createViewModelInstance("main-list-child"); | |
| auto childGrandChild = childInstance->propertyValue("grandchild") | |
| ->as<ViewModelInstanceViewModel>() | |
| ->referenceViewModelInstance() | |
| .get(); | |
| auto childGrandChildLabel = childGrandChild->propertyValue("label") | |
| ->as<ViewModelInstanceString>(); | |
| childGrandChildLabel->propertyValue("new label"); | |
| // Second append it to the list | |
| auto listItem = make_rcp<ViewModelInstanceListItem>(); | |
| listItem->viewModelInstance(childInstance); | |
| lis->addItem(listItem); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| // Replace the view model property and expect the instance to be updated | |
| silver.addFrame(); | |
| auto grandChildInstance = | |
| file->createViewModelInstance("main-list-grandchild"); | |
| auto labelProp = grandChildInstance->propertyValue("label") | |
| ->as<ViewModelInstanceString>(); | |
| labelProp->propertyValue("modified 2"); | |
| childInstance->replaceViewModelByName("grandchild", grandChildInstance); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("replace_vm_instance-double-nest")); | |
| } | |
| TEST_CASE("Pointer drag event", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/drag_event.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| REQUIRE(artboard != nullptr); | |
| silver.frameSize(artboard->width(), artboard->height()); | |
| auto stateMachine = artboard->stateMachineAt(0); | |
| auto vmi = file->createViewModelInstance(artboard.get()->viewModelId(), 0); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| // Clicking on a square without moving will trigger the click on the nested | |
| // artboard | |
| stateMachine->pointerDown(rive::Vec2D(250, 250)); | |
| stateMachine->pointerUp(rive::Vec2D(250, 250)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| auto coord = 250.0f; | |
| stateMachine->pointerDown(rive::Vec2D(coord, coord)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| // Drag gesture works and click is cancelled | |
| while (coord > 50) | |
| { | |
| silver.addFrame(); | |
| stateMachine->pointerMove(rive::Vec2D(coord, coord)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| coord -= 10; | |
| } | |
| stateMachine->pointerUp(rive::Vec2D(coord, coord)); | |
| silver.addFrame(); | |
| // Clicking again on a square without moving will trigger the click on the | |
| // nested artboard | |
| stateMachine->pointerDown(rive::Vec2D(coord, coord)); | |
| stateMachine->advanceAndApply(0.1f); | |
| stateMachine->pointerUp(rive::Vec2D(coord, coord)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("drag_event")); | |
| } | |
| TEST_CASE("Recursive data binding artboards are skipped", "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/recursive_data_bind.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| REQUIRE(artboard != nullptr); | |
| silver.frameSize(artboard->width(), artboard->height()); | |
| auto stateMachine = artboard->stateMachineAt(0); | |
| auto vmi = file->createViewModelInstance(artboard.get()->viewModelId(), 0); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| stateMachine->pointerDown(rive::Vec2D(249.0f, 430.0f)); | |
| stateMachine->pointerUp(rive::Vec2D(249.0f, 430.0f)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| stateMachine->pointerDown(rive::Vec2D(391.0f, 430.0f)); | |
| stateMachine->pointerUp(rive::Vec2D(391.0f, 430.0f)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| // This action will bind to a recursive artboard and should be skipped | |
| stateMachine->pointerDown(rive::Vec2D(107.0f, 430.0f)); | |
| stateMachine->pointerUp(rive::Vec2D(107.0f, 430.0f)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| stateMachine->pointerDown(rive::Vec2D(249.0f, 430.0f)); | |
| stateMachine->pointerUp(rive::Vec2D(249.0f, 430.0f)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| stateMachine->pointerDown(rive::Vec2D(391.0f, 430.0f)); | |
| stateMachine->pointerUp(rive::Vec2D(391.0f, 430.0f)); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("recursive_data_bind")); | |
| } | |
| TEST_CASE("Collapsable data binds get added when object is uncollapsed", | |
| "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/collapsable_data_binding.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| REQUIRE(artboard != nullptr); | |
| silver.frameSize(artboard->width(), artboard->height()); | |
| auto stateMachine = artboard->stateMachineAt(0); | |
| auto vmi = file->createViewModelInstance(artboard.get()->viewModelId(), 0); | |
| auto soloIndexProp = | |
| vmi->propertyValue("soloIndex")->as<ViewModelInstanceNumber>(); | |
| auto colorProp = vmi->propertyValue("col")->as<ViewModelInstanceColor>(); | |
| stateMachine->bindViewModelInstance(vmi); | |
| stateMachine->advanceAndApply(0.1f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| auto redColor = (255 << 24) | (255 << 16); | |
| // Setting the red color should update both data binds although one is | |
| // soloed | |
| colorProp->propertyValue(redColor); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| soloIndexProp->propertyValue(1); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| auto greenColor = (255 << 24) | (255 << 8); | |
| colorProp->propertyValue(greenColor); | |
| soloIndexProp->propertyValue(0); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| soloIndexProp->propertyValue(1); | |
| stateMachine->advanceAndApply(0.1f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("collapsable_data_binding")); | |
| } | |
| // Note to this test for future reference: This test is passing because when an | |
| // artboard list is initialized, it populates its nested artboards only after | |
| // option C has advanced a second time. This causes tryChangeState to run before | |
| // advancing, and that allows the state to be available for the first advance to | |
| // mix its blend values. That's not the case for nested artboard, so if any of | |
| // these premises changes in the future, this test would catch the change. | |
| TEST_CASE( | |
| "Virtualized list with blended animations as initial state correctly render their mixed values", | |
| "[silver]") | |
| { | |
| RandomProvider::clearRandoms(); | |
| REQUIRE(RandomProvider::totalCalls() == 0); | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/virtualize_blendmode.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.016f); | |
| 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("virtualize_blendmode")); | |
| } | |
| TEST_CASE("Advance two consecutive blend modes and apply inputs", "[silver]") | |
| { | |
| RandomProvider::clearRandoms(); | |
| REQUIRE(RandomProvider::totalCalls() == 0); | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/advance_blend_mode.riv", &silver); | |
| auto artboard = file->artboardNamed("main-inputs"); | |
| 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.0f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("advance_blend_mode-inputs")); | |
| } | |
| TEST_CASE("Advance two consecutive blend modes and apply view model", | |
| "[silver]") | |
| { | |
| RandomProvider::clearRandoms(); | |
| REQUIRE(RandomProvider::totalCalls() == 0); | |
| SerializingFactory silver; | |
| auto file = ReadRiveFile("assets/advance_blend_mode.riv", &silver); | |
| auto artboard = file->artboardNamed("main-vms"); | |
| 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.0f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| CHECK(silver.matches("advance_blend_mode-vms")); | |
| } | |
| TEST_CASE("Test State machine transition conditions based on artboards", | |
| "[silver]") | |
| { | |
| SerializingFactory silver; | |
| auto file = | |
| ReadRiveFile("assets/transition_artboard_condition_test.riv", &silver); | |
| auto artboard = file->artboardDefault(); | |
| 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.0f); | |
| auto renderer = silver.makeRenderer(); | |
| artboard->draw(renderer.get()); | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| int frames = (int)(1.0f / 0.016f); | |
| for (int i = 0; i < frames; i++) | |
| { | |
| silver.addFrame(); | |
| stateMachine->advanceAndApply(0.016f); | |
| artboard->draw(renderer.get()); | |
| } | |
| CHECK(silver.matches("transition_artboard_condition_test")); | |
| } |