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");