feat: add count and query APIs for events and text runs at the Artboard level, and expose in WASM Will add some tests, but want to get validation before I go much further that this is the approach we want to take to expose Events and Text Runs for a given artboard at the `rive::Artboard` level, similar to the APIs for getting the linear animation/state machine from an `Artboard`. Adding to `ArtboardImporter` seemed most appropriate but not sure there's any caveats or better ways to plumb through adding the events/runs. Maybe @luigi-rosso @philter ? Decided on exposing `textValueRunCount()`/`textValueRunAt()` and `eventCount()`/`eventAt()` APIs on the Artboard at the cpp level to follow existing patterns. This should make it simple for other runtimes to adopt too. Another alternative is just doing an empty `find<rive::TextValueRun>()`/`find<rive::Event>()` call at each runtime level which looks like it returns a list of all the objects. TODO: - [x] Add tests - [x] Check WASM size before/after new bindings Diffs= 22077beda feat: add count and query APIs for events and text runs at the Artboard level, and expose in WASM (#6043) Co-authored-by: Zachary Plata <plata.zach@gmail.com>
diff --git a/.rive_head b/.rive_head index aec9b2c..8da8b8d 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -f95f54140657f8b8726392a3d0b4036bb4ad70fb +22077bedae4c277e5e8a81c732167ab5d559917c
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index b53430c..e21e34a 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp
@@ -9,6 +9,8 @@ #include "rive/math/aabb.hpp" #include "rive/renderer.hpp" #include "rive/shapes/shape_paint_container.hpp" +#include "rive/text/text_value_run.hpp" +#include "rive/event.hpp" #include <queue> #include <vector> @@ -27,6 +29,8 @@ class Scene; class StateMachineInstance; class Joystick; +class TextValueRun; +class Event; class Artboard : public ArtboardBase, public CoreContext, public ShapePaintContainer { @@ -38,6 +42,8 @@ std::vector<Core*> m_Objects; std::vector<LinearAnimation*> m_Animations; std::vector<StateMachine*> m_StateMachines; + std::vector<TextValueRun*> m_TextValueRuns; + std::vector<Event*> m_Events; std::vector<Component*> m_DependencyOrder; std::vector<Drawable*> m_Drawables; std::vector<DrawTarget*> m_DrawTargets; @@ -65,6 +71,8 @@ void addObject(Core* object); void addAnimation(LinearAnimation* object); void addStateMachine(StateMachine* object); + void addTextValueRun(TextValueRun* object); + void addEvent(Event* object); public: Artboard() {} @@ -146,6 +154,12 @@ size_t stateMachineCount() const { return m_StateMachines.size(); } std::string stateMachineNameAt(size_t index) const; + size_t textValueRunCount() const { return m_TextValueRuns.size(); } + TextValueRun* textValueRunAt(size_t index) const; + + size_t eventCount() const { return m_Events.size(); } + Event* eventAt(size_t index) const; + LinearAnimation* firstAnimation() const { return animation(0); } LinearAnimation* animation(const std::string& name) const; LinearAnimation* animation(size_t index) const; @@ -191,6 +205,14 @@ { artboardClone->m_StateMachines.push_back(stateMachine); } + for (auto textRun : m_TextValueRuns) + { + artboardClone->m_TextValueRuns.push_back(textRun); + } + for (auto event : m_Events) + { + artboardClone->m_Events.push_back(event); + } if (artboardClone->initialize() != StatusCode::Ok) {
diff --git a/include/rive/event.hpp b/include/rive/event.hpp index 743c6c5..906f562 100644 --- a/include/rive/event.hpp +++ b/include/rive/event.hpp
@@ -8,6 +8,7 @@ { public: void trigger(const CallbackData& value) override; + StatusCode import(ImportStack& importStack) override; }; } // namespace rive
diff --git a/include/rive/importers/artboard_importer.hpp b/include/rive/importers/artboard_importer.hpp index a6fc955..b9fba12 100644 --- a/include/rive/importers/artboard_importer.hpp +++ b/include/rive/importers/artboard_importer.hpp
@@ -9,6 +9,8 @@ class Artboard; class LinearAnimation; class StateMachine; +class TextValueRun; +class Event; class ArtboardImporter : public ImportStackObject { private: @@ -19,6 +21,8 @@ void addComponent(Core* object); void addAnimation(LinearAnimation* animation); void addStateMachine(StateMachine* stateMachine); + void addTextValueRun(TextValueRun* textValueRun); + void addEvent(Event* event); StatusCode resolve() override; const Artboard* artboard() const { return m_Artboard; }
diff --git a/include/rive/text/text_value_run.hpp b/include/rive/text/text_value_run.hpp index 3bdca17..0807b34 100644 --- a/include/rive/text/text_value_run.hpp +++ b/include/rive/text/text_value_run.hpp
@@ -10,6 +10,7 @@ public: StatusCode onAddedClean(CoreContext* context) override; StatusCode onAddedDirty(CoreContext* context) override; + StatusCode import(ImportStack& importStack) override; TextStyle* style() { return m_style; } uint32_t offset() const;
diff --git a/src/artboard.cpp b/src/artboard.cpp index 8b37215..457143b 100644 --- a/src/artboard.cpp +++ b/src/artboard.cpp
@@ -18,6 +18,8 @@ #include "rive/joystick.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/shapes/shape.hpp" +#include "rive/text/text_value_run.hpp" +#include "rive/event.hpp" #include <stack> #include <unordered_map> @@ -368,6 +370,10 @@ void Artboard::addStateMachine(StateMachine* object) { m_StateMachines.push_back(object); } +void Artboard::addTextValueRun(TextValueRun* object) { m_TextValueRuns.push_back(object); } + +void Artboard::addEvent(Event* object) { m_Events.push_back(object); } + Core* Artboard::resolve(uint32_t id) const { if (id >= static_cast<int>(m_Objects.size())) @@ -709,6 +715,24 @@ return index; } +TextValueRun* Artboard::textValueRunAt(size_t index) const +{ + if (index >= m_TextValueRuns.size()) + { + return nullptr; + } + return m_TextValueRuns[index]; +} + +Event* Artboard::eventAt(size_t index) const +{ + if (index >= m_Events.size()) + { + return nullptr; + } + return m_Events[index]; +} + // std::unique_ptr<ArtboardInstance> Artboard::instance() const // { // std::unique_ptr<ArtboardInstance> artboardClone(new ArtboardInstance);
diff --git a/src/event.cpp b/src/event.cpp index 84be0ee..39dade4 100644 --- a/src/event.cpp +++ b/src/event.cpp
@@ -1,9 +1,22 @@ #include "rive/event.hpp" #include "rive/animation/state_machine_instance.hpp" +#include "rive/artboard.hpp" +#include "rive/importers/artboard_importer.hpp" using namespace rive; void Event::trigger(const CallbackData& value) { value.context()->reportEvent(this, value.delaySeconds()); +} + +StatusCode Event::import(ImportStack& importStack) +{ + auto artboardImporter = importStack.latest<ArtboardImporter>(ArtboardBase::typeKey); + if (artboardImporter == nullptr) + { + return StatusCode::MissingObject; + } + artboardImporter->addEvent(this); + return Super::import(importStack); } \ No newline at end of file
diff --git a/src/importers/artboard_importer.cpp b/src/importers/artboard_importer.cpp index db008ba..ccb524c 100644 --- a/src/importers/artboard_importer.cpp +++ b/src/importers/artboard_importer.cpp
@@ -2,6 +2,8 @@ #include "rive/importers/artboard_importer.hpp" #include "rive/animation/linear_animation.hpp" #include "rive/animation/state_machine.hpp" +#include "rive/text/text_value_run.hpp" +#include "rive/event.hpp" #include "rive/artboard.hpp" using namespace rive; @@ -20,6 +22,13 @@ m_Artboard->addStateMachine(stateMachine); } +void ArtboardImporter::addTextValueRun(TextValueRun* textValueRun) +{ + m_Artboard->addTextValueRun(textValueRun); +} + +void ArtboardImporter::addEvent(Event* event) { m_Artboard->addEvent(event); } + StatusCode ArtboardImporter::resolve() { return m_Artboard->initialize(); } bool ArtboardImporter::readNullObject()
diff --git a/src/text/text_value_run.cpp b/src/text/text_value_run.cpp index f46c83b..5093ca2 100644 --- a/src/text/text_value_run.cpp +++ b/src/text/text_value_run.cpp
@@ -3,6 +3,7 @@ #include "rive/text/text_style.hpp" #include "rive/text/text_value_run.hpp" #include "rive/artboard.hpp" +#include "rive/importers/artboard_importer.hpp" using namespace rive; @@ -43,6 +44,17 @@ return StatusCode::Ok; } +StatusCode TextValueRun::import(ImportStack& importStack) +{ + auto artboardImporter = importStack.latest<ArtboardImporter>(ArtboardBase::typeKey); + if (artboardImporter == nullptr) + { + return StatusCode::MissingObject; + } + artboardImporter->addTextValueRun(this); + return Super::import(importStack); +} + void TextValueRun::styleIdChanged() { auto coreObject = artboard()->resolve(styleId());
diff --git a/test/state_machine_event_test.cpp b/test/state_machine_event_test.cpp index b801a03..1b06681 100644 --- a/test/state_machine_event_test.cpp +++ b/test/state_machine_event_test.cpp
@@ -120,6 +120,24 @@ REQUIRE(switchButton->value() == true); } +TEST_CASE("can query for all rive events", "[events]") +{ + auto file = ReadRiveFile("../../test/assets/event_on_listener.riv"); + auto artboard = file->artboard(); + + auto eventCount = artboard->eventCount(); + REQUIRE(eventCount == 4); +} + +TEST_CASE("can query for a rive event at a given index", "[events]") +{ + auto file = ReadRiveFile("../../test/assets/event_on_listener.riv"); + auto artboard = file->artboard(); + + auto event = artboard->eventAt(0); + REQUIRE(event->name() == "Somewhere.com"); +} + TEST_CASE("events load correctly on a listener", "[events]") { auto file = ReadRiveFile("../../test/assets/event_on_listener.riv");
diff --git a/test/text_test.cpp b/test/text_test.cpp index 701fcb2..c009a05 100644 --- a/test/text_test.cpp +++ b/test/text_test.cpp
@@ -28,6 +28,24 @@ artboard->draw(&renderer); } +TEST_CASE("can query for all text runs", "[text]") +{ + auto file = ReadRiveFile("../../test/assets/new_text.riv"); + auto artboard = file->artboard(); + + auto textRunCount = artboard->textValueRunCount(); + REQUIRE(textRunCount == 22); +} + +TEST_CASE("can query for a text run at a given index", "[text]") +{ + auto file = ReadRiveFile("../../test/assets/hello_world.riv"); + auto artboard = file->artboard(); + + auto textRun = artboard->textValueRunAt(0); + REQUIRE(textRun->text() == "Hello World!"); +} + TEST_CASE("simple text loads", "[text]") { auto file = ReadRiveFile("../../test/assets/hello_world.riv");