feature: scripted listener actions (#11468) f3a89390cb Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head index e1626a4..c880cd3 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -9112280455e25db4649d446601f443c763151649 +f3a89390cb428a5ea841d21de91f9cb2adc312df
diff --git a/dev/defs/animation/scripted_listener_action.json b/dev/defs/animation/scripted_listener_action.json new file mode 100644 index 0000000..8ee9d6d --- /dev/null +++ b/dev/defs/animation/scripted_listener_action.json
@@ -0,0 +1,20 @@ +{ + "name": "ScriptedListenerAction", + "key": { + "int": 646, + "string": "scriptedlisteneraction" + }, + "extends": "animation/listener_action.json", + "properties": { + "scriptAssetId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 930, + "string": "scriptassetid" + } + } + } +} \ No newline at end of file
diff --git a/include/rive/animation/listener_action.hpp b/include/rive/animation/listener_action.hpp index 691ab0f..a66df47 100644 --- a/include/rive/animation/listener_action.hpp +++ b/include/rive/animation/listener_action.hpp
@@ -12,7 +12,8 @@ StatusCode import(ImportStack& importStack) override; virtual void perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const = 0; + Vec2D previousPosition, + int pointerId) const = 0; }; } // namespace rive
diff --git a/include/rive/animation/listener_align_target.hpp b/include/rive/animation/listener_align_target.hpp index 2a150d6..aacae88 100644 --- a/include/rive/animation/listener_align_target.hpp +++ b/include/rive/animation/listener_align_target.hpp
@@ -9,7 +9,8 @@ public: void perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const override; + Vec2D previousPosition, + int pointerId) const override; }; } // namespace rive
diff --git a/include/rive/animation/listener_bool_change.hpp b/include/rive/animation/listener_bool_change.hpp index a6f25c2..2d05804 100644 --- a/include/rive/animation/listener_bool_change.hpp +++ b/include/rive/animation/listener_bool_change.hpp
@@ -12,7 +12,8 @@ bool validateNestedInputType(const NestedInput* input) const override; void perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const override; + Vec2D previousPosition, + int pointerId) const override; }; } // namespace rive
diff --git a/include/rive/animation/listener_fire_event.hpp b/include/rive/animation/listener_fire_event.hpp index 4e4a7d2..40fadbf 100644 --- a/include/rive/animation/listener_fire_event.hpp +++ b/include/rive/animation/listener_fire_event.hpp
@@ -9,7 +9,8 @@ public: void perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const override; + Vec2D previousPosition, + int pointerId) const override; }; } // namespace rive
diff --git a/include/rive/animation/listener_number_change.hpp b/include/rive/animation/listener_number_change.hpp index 613d7d3..619bdfb 100644 --- a/include/rive/animation/listener_number_change.hpp +++ b/include/rive/animation/listener_number_change.hpp
@@ -12,7 +12,8 @@ bool validateNestedInputType(const NestedInput* input) const override; void perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const override; + Vec2D previousPosition, + int pointerId) const override; }; } // namespace rive
diff --git a/include/rive/animation/listener_trigger_change.hpp b/include/rive/animation/listener_trigger_change.hpp index 005f472..838b332 100644 --- a/include/rive/animation/listener_trigger_change.hpp +++ b/include/rive/animation/listener_trigger_change.hpp
@@ -12,7 +12,8 @@ bool validateNestedInputType(const NestedInput* input) const override; void perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const override; + Vec2D previousPosition, + int pointerId) const override; }; } // namespace rive
diff --git a/include/rive/animation/listener_viewmodel_change.hpp b/include/rive/animation/listener_viewmodel_change.hpp index a58ef49..6f4c8eb 100644 --- a/include/rive/animation/listener_viewmodel_change.hpp +++ b/include/rive/animation/listener_viewmodel_change.hpp
@@ -11,7 +11,8 @@ ~ListenerViewModelChange(); void perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const override; + Vec2D previousPosition, + int pointerId) const override; StatusCode import(ImportStack& importStack) override; private:
diff --git a/include/rive/animation/scripted_listener_action.hpp b/include/rive/animation/scripted_listener_action.hpp new file mode 100644 index 0000000..b464337 --- /dev/null +++ b/include/rive/animation/scripted_listener_action.hpp
@@ -0,0 +1,36 @@ +#ifndef _RIVE_SCRIPTED_LISTENER_ACTION_HPP_ +#define _RIVE_SCRIPTED_LISTENER_ACTION_HPP_ +#include "rive/generated/animation/scripted_listener_action_base.hpp" +#include "rive/scripted/scripted_object.hpp" +#include <stdio.h> +namespace rive +{ +class ScriptedListenerAction : public ScriptedListenerActionBase, + public ScriptedObject +{ +public: + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition, + int pointerId) const override; + void performStateful(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition, + int pointerId) const; + + uint32_t assetId() override { return scriptAssetId(); } + bool addScriptedDirt(ComponentDirt value, bool recurse = false) override + { + return false; + } + ScriptProtocol scriptProtocol() override + { + return ScriptProtocol::listenerAction; + } + Component* component() override { return nullptr; } + StatusCode import(ImportStack& importStack) override; + Core* clone() const override; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp index 840c77e..641630c 100644 --- a/include/rive/animation/state_machine_instance.hpp +++ b/include/rive/animation/state_machine_instance.hpp
@@ -40,6 +40,7 @@ class BindableProperty; class HitDrawable; class ListenerViewModel; +class ScriptedListenerAction; typedef void (*DataBindChanged)(); #ifdef WITH_RIVE_TOOLS @@ -205,6 +206,7 @@ bool hasListeners() { return m_hitComponents.size() > 0; } void clearDataContext(); void internalDataContext(DataContext* dataContext); + ScriptedObject* scriptedObject(const ScriptedObject*); #ifdef TESTING size_t hitComponentsCount() { return m_hitComponents.size(); }; HitComponent* hitComponent(size_t index) @@ -238,6 +240,8 @@ std::vector<ListenerViewModel*> m_reportingListenerViewModels; std::unordered_map<BindableProperty*, BindableProperty*> m_bindablePropertyInstances; + std::unordered_map<const ScriptedObject*, ScriptedObject*> + m_scriptedListenerActionsMap; std::unordered_map<BindableProperty*, DataBind*> m_bindableDataBindsToTarget; std::unordered_map<BindableProperty*, DataBind*>
diff --git a/include/rive/animation/state_machine_listener.hpp b/include/rive/animation/state_machine_listener.hpp index 27e62f6..ac1c9a6 100644 --- a/include/rive/animation/state_machine_listener.hpp +++ b/include/rive/animation/state_machine_listener.hpp
@@ -31,7 +31,8 @@ void performChanges(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const; + Vec2D previousPosition, + int pointerId) const; void decodeViewModelPathIds(Span<const uint8_t> value) override; void copyViewModelPathIds(const StateMachineListenerBase& object) override; std::vector<uint32_t> viewModelPathIdsBuffer() const;
diff --git a/include/rive/assets/script_asset.hpp b/include/rive/assets/script_asset.hpp index 8ae0501..6de0360 100644 --- a/include/rive/assets/script_asset.hpp +++ b/include/rive/assets/script_asset.hpp
@@ -25,7 +25,8 @@ node, layout, converter, - pathEffect + pathEffect, + listenerAction }; #ifdef WITH_RIVE_SCRIPTING
diff --git a/include/rive/generated/animation/scripted_listener_action_base.hpp b/include/rive/generated/animation/scripted_listener_action_base.hpp new file mode 100644 index 0000000..9a92033 --- /dev/null +++ b/include/rive/generated/animation/scripted_listener_action_base.hpp
@@ -0,0 +1,71 @@ +#ifndef _RIVE_SCRIPTED_LISTENER_ACTION_BASE_HPP_ +#define _RIVE_SCRIPTED_LISTENER_ACTION_BASE_HPP_ +#include "rive/animation/listener_action.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class ScriptedListenerActionBase : public ListenerAction +{ +protected: + typedef ListenerAction Super; + +public: + static const uint16_t typeKey = 646; + + /// Helper to quickly determine if a core object extends another without + /// RTTI at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ScriptedListenerActionBase::typeKey: + case ListenerActionBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t scriptAssetIdPropertyKey = 930; + +protected: + uint32_t m_ScriptAssetId = -1; + +public: + inline uint32_t scriptAssetId() const { return m_ScriptAssetId; } + void scriptAssetId(uint32_t value) + { + if (m_ScriptAssetId == value) + { + return; + } + m_ScriptAssetId = value; + scriptAssetIdChanged(); + } + + Core* clone() const override; + void copy(const ScriptedListenerActionBase& object) + { + m_ScriptAssetId = object.m_ScriptAssetId; + ListenerAction::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case scriptAssetIdPropertyKey: + m_ScriptAssetId = CoreUintType::deserialize(reader); + return true; + } + return ListenerAction::deserialize(propertyKey, reader); + } + +protected: + virtual void scriptAssetIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index f868968..4a541ae 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp
@@ -50,6 +50,7 @@ #include "rive/animation/nested_simple_animation.hpp" #include "rive/animation/nested_state_machine.hpp" #include "rive/animation/nested_trigger.hpp" +#include "rive/animation/scripted_listener_action.hpp" #include "rive/animation/state_machine.hpp" #include "rive/animation/state_machine_bool.hpp" #include "rive/animation/state_machine_component.hpp" @@ -477,6 +478,8 @@ return new AnimationState(); case NestedTriggerBase::typeKey: return new NestedTrigger(); + case ScriptedListenerActionBase::typeKey: + return new ScriptedListenerAction(); case KeyedObjectBase::typeKey: return new KeyedObject(); case AnimationBase::typeKey: @@ -1194,6 +1197,9 @@ case NestedInputBase::inputIdPropertyKey: object->as<NestedInputBase>()->inputId(value); break; + case ScriptedListenerActionBase::scriptAssetIdPropertyKey: + object->as<ScriptedListenerActionBase>()->scriptAssetId(value); + break; case KeyedObjectBase::objectIdPropertyKey: object->as<KeyedObjectBase>()->objectId(value); break; @@ -2737,6 +2743,9 @@ return object->as<AnimationStateBase>()->animationId(); case NestedInputBase::inputIdPropertyKey: return object->as<NestedInputBase>()->inputId(); + case ScriptedListenerActionBase::scriptAssetIdPropertyKey: + return object->as<ScriptedListenerActionBase>() + ->scriptAssetId(); case KeyedObjectBase::objectIdPropertyKey: return object->as<KeyedObjectBase>()->objectId(); case BlendAnimationBase::animationIdPropertyKey: @@ -3725,6 +3734,7 @@ case ListenerInputChangeBase::nestedInputIdPropertyKey: case AnimationStateBase::animationIdPropertyKey: case NestedInputBase::inputIdPropertyKey: + case ScriptedListenerActionBase::scriptAssetIdPropertyKey: case KeyedObjectBase::objectIdPropertyKey: case BlendAnimationBase::animationIdPropertyKey: case BlendAnimationDirectBase::inputIdPropertyKey: @@ -4377,6 +4387,8 @@ return object->is<AnimationStateBase>(); case NestedInputBase::inputIdPropertyKey: return object->is<NestedInputBase>(); + case ScriptedListenerActionBase::scriptAssetIdPropertyKey: + return object->is<ScriptedListenerActionBase>(); case KeyedObjectBase::objectIdPropertyKey: return object->is<KeyedObjectBase>(); case BlendAnimationBase::animationIdPropertyKey:
diff --git a/include/rive/scripted/scripted_object.hpp b/include/rive/scripted/scripted_object.hpp index 568a171..c712716 100644 --- a/include/rive/scripted/scripted_object.hpp +++ b/include/rive/scripted/scripted_object.hpp
@@ -33,6 +33,8 @@ #ifdef WITH_RIVE_TOOLS bool hasValidVM(); #endif +private: + DataContext* m_dataContext = nullptr; public: virtual ~ScriptedObject() { scriptDispose(); } @@ -48,7 +50,8 @@ void scriptUpdate(); void reinit(); virtual void markNeedsUpdate(); - virtual DataContext* dataContext() { return nullptr; } + virtual DataContext* dataContext() { return m_dataContext; } + void dataContext(DataContext* value) { m_dataContext = value; } #ifdef WITH_RIVE_SCRIPTING virtual bool scriptInit(lua_State* state); lua_State* state() { return m_state; }
diff --git a/src/animation/listener_align_target.cpp b/src/animation/listener_align_target.cpp index 415321e..4cc53e4 100644 --- a/src/animation/listener_align_target.cpp +++ b/src/animation/listener_align_target.cpp
@@ -7,7 +7,8 @@ void ListenerAlignTarget::perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const + Vec2D previousPosition, + int pointerId) const { auto coreTarget = stateMachineInstance->artboard()->resolve(targetId()); if (coreTarget == nullptr || !coreTarget->is<Node>())
diff --git a/src/animation/listener_bool_change.cpp b/src/animation/listener_bool_change.cpp index 20bdf0e..e1c1c12 100644 --- a/src/animation/listener_bool_change.cpp +++ b/src/animation/listener_bool_change.cpp
@@ -26,7 +26,8 @@ void ListenerBoolChange::perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const + Vec2D previousPosition, + int pointerId) const { if (nestedInputId() != Core::emptyId) {
diff --git a/src/animation/listener_fire_event.cpp b/src/animation/listener_fire_event.cpp index bd6d074..e1821d3 100644 --- a/src/animation/listener_fire_event.cpp +++ b/src/animation/listener_fire_event.cpp
@@ -6,7 +6,8 @@ void ListenerFireEvent::perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const + Vec2D previousPosition, + int pointerId) const { auto coreEvent = stateMachineInstance->artboard()->resolve(eventId()); if (coreEvent == nullptr || !coreEvent->is<Event>())
diff --git a/src/animation/listener_number_change.cpp b/src/animation/listener_number_change.cpp index a57d0df..c0716d2 100644 --- a/src/animation/listener_number_change.cpp +++ b/src/animation/listener_number_change.cpp
@@ -29,7 +29,8 @@ void ListenerNumberChange::perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const + Vec2D previousPosition, + int pointerId) const { if (nestedInputId() != Core::emptyId) {
diff --git a/src/animation/listener_trigger_change.cpp b/src/animation/listener_trigger_change.cpp index f8d8584..178d199 100644 --- a/src/animation/listener_trigger_change.cpp +++ b/src/animation/listener_trigger_change.cpp
@@ -30,7 +30,8 @@ void ListenerTriggerChange::perform(StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const + Vec2D previousPosition, + int pointerId) const { if (nestedInputId() != Core::emptyId) {
diff --git a/src/animation/listener_viewmodel_change.cpp b/src/animation/listener_viewmodel_change.cpp index 5244d9c..c4bd477 100644 --- a/src/animation/listener_viewmodel_change.cpp +++ b/src/animation/listener_viewmodel_change.cpp
@@ -37,7 +37,8 @@ void ListenerViewModelChange::perform( StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const + Vec2D previousPosition, + int pointerId) const { // Get the bindable property instance from the state machine instance // context
diff --git a/src/animation/scripted_listener_action.cpp b/src/animation/scripted_listener_action.cpp new file mode 100644 index 0000000..4b3b959 --- /dev/null +++ b/src/animation/scripted_listener_action.cpp
@@ -0,0 +1,86 @@ +#include "rive/animation/scripted_listener_action.hpp" +#include "rive/animation/state_machine_instance.hpp" + +using namespace rive; + +// Note: performStateful is the actual instance of the ScriptedListenerAction +// that will run the script. perform itself will look for the map between the +// stateless and the stateful instances of this class. +void ScriptedListenerAction::performStateful( + StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition, + int pointerId) const +{ +#ifdef WITH_RIVE_SCRIPTING + if (m_state == nullptr) + { + return; + } + // Stack: [] + rive_lua_pushRef(m_state, m_self); + // Stack: [self] + lua_getfield(m_state, -1, "perform"); + + // Stack: [self, field] + lua_pushvalue(m_state, -2); + + // Stack: [self, field, self] + lua_newrive<ScriptedPointerEvent>(m_state, pointerId, position); + + // Stack: [self, field, self, pointerEvent] + if (static_cast<lua_Status>(rive_lua_pcall(m_state, 2, 0)) == LUA_OK) + { + rive_lua_pop(m_state, 1); + } + else + { + rive_lua_pop(m_state, 2); + } +#endif +} + +void ScriptedListenerAction::perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition, + int pointerId) const +{ +#ifdef WITH_RIVE_SCRIPTING + auto scriptedObject = stateMachineInstance->scriptedObject(this); + if (scriptedObject != nullptr) + { + auto statefulListenerAction = + static_cast<ScriptedListenerAction*>(scriptedObject); + statefulListenerAction->performStateful(stateMachineInstance, + position, + previousPosition, + pointerId); + } +#endif +} + +StatusCode ScriptedListenerAction::import(ImportStack& importStack) +{ + auto result = registerReferencer(importStack); + if (result != StatusCode::Ok) + { + return result; + } + return Super::import(importStack); +} + +Core* ScriptedListenerAction::clone() const +{ + ScriptedListenerAction* twin = + ScriptedListenerActionBase::clone()->as<ScriptedListenerAction>(); + if (m_fileAsset != nullptr) + { + twin->setAsset(m_fileAsset); + } + for (auto prop : m_customProperties) + { + auto clonedValue = prop->clone()->as<CustomProperty>(); + twin->addProperty(clonedValue); + } + return twin; +} \ No newline at end of file
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp index e7d02ee..964b7a2 100644 --- a/src/animation/state_machine_instance.cpp +++ b/src/animation/state_machine_instance.cpp
@@ -19,6 +19,8 @@ #include "rive/animation/state_machine_trigger.hpp" #include "rive/animation/state_machine.hpp" #include "rive/animation/state_transition.hpp" +#include "rive/animation/listener_action.hpp" +#include "rive/animation/scripted_listener_action.hpp" #include "rive/animation/transition_condition.hpp" #include "rive/animation/transition_comparator.hpp" #include "rive/animation/transition_property_viewmodel_comparator.hpp" @@ -1480,9 +1482,41 @@ this); m_hitComponents.push_back(std::move(hc)); } + // Initialize local instances of ScriptedListenerActions + for (std::size_t i = 0; i < machine->listenerCount(); i++) + { + auto listener = machine->listener(i); + + for (std::size_t j = 0; j < listener->actionCount(); j++) + { + auto action = listener->action(j); + if (action->is<ScriptedListenerAction>()) + { + auto scriptedListenerAction = + action->as<ScriptedListenerAction>(); + auto scriptedListenerActionClone = + static_cast<ScriptedListenerAction*>( + scriptedListenerAction->clone()); + scriptedListenerActionClone->reinit(); + m_scriptedListenerActionsMap[scriptedListenerAction] = + scriptedListenerActionClone; + } + } + } sortHitComponents(); } +ScriptedObject* StateMachineInstance::scriptedObject( + const ScriptedObject* source) +{ + auto itr = m_scriptedListenerActionsMap.find(source); + if (itr != m_scriptedListenerActionsMap.end()) + { + return itr->second; + } + return nullptr; +} + StateMachineInstance::~StateMachineInstance() { unbind(); @@ -1506,6 +1540,12 @@ delete listenerViewModel; } m_bindablePropertyInstances.clear(); + for (auto& pair : m_scriptedListenerActionsMap) + { + delete pair.second; + pair.second = nullptr; + } + m_scriptedListenerActionsMap.clear(); } void StateMachineInstance::removeEventListeners() @@ -1816,6 +1856,10 @@ { listenerViewModel->bindFromContext(dataContext); } + for (auto& scriptedObjectItr : m_scriptedListenerActionsMap) + { + scriptedObjectItr.second->dataContext(dataContext); + } } void StateMachineInstance::rebind() @@ -1955,7 +1999,8 @@ { listenerViewModel->listener()->performChanges(this, Vec2D(), - Vec2D()); + Vec2D(), + 0); } } } @@ -2011,7 +2056,7 @@ sourceArtboard->resolve(listener->eventId()); if (listenerEvent == event.event()) { - listener->performChanges(this, Vec2D(), Vec2D()); + listener->performChanges(this, Vec2D(), Vec2D(), 0); break; } }
diff --git a/src/animation/state_machine_listener.cpp b/src/animation/state_machine_listener.cpp index 3595f6b..7b39e3a 100644 --- a/src/animation/state_machine_listener.cpp +++ b/src/animation/state_machine_listener.cpp
@@ -44,11 +44,15 @@ void StateMachineListener::performChanges( StateMachineInstance* stateMachineInstance, Vec2D position, - Vec2D previousPosition) const + Vec2D previousPosition, + int pointerId) const { for (auto& action : m_actions) { - action->perform(stateMachineInstance, position, previousPosition); + action->perform(stateMachineInstance, + position, + previousPosition, + pointerId); } }
diff --git a/src/assets/script_asset.cpp b/src/assets/script_asset.cpp index c6eb4f6..1542c46 100644 --- a/src/assets/script_asset.cpp +++ b/src/assets/script_asset.cpp
@@ -66,7 +66,8 @@ if (scriptProtocol == ScriptProtocol::node || scriptProtocol == ScriptProtocol::layout || scriptProtocol == ScriptProtocol::converter || - scriptProtocol == ScriptProtocol::pathEffect) + scriptProtocol == ScriptProtocol::pathEffect || + scriptProtocol == ScriptProtocol::listenerAction) { if (static_cast<lua_Type>(lua_getfield(state, -1, "update")) == LUA_TFUNCTION)
diff --git a/src/generated/animation/scripted_listener_action_base.cpp b/src/generated/animation/scripted_listener_action_base.cpp new file mode 100644 index 0000000..6ac036e --- /dev/null +++ b/src/generated/animation/scripted_listener_action_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/animation/scripted_listener_action_base.hpp" +#include "rive/animation/scripted_listener_action.hpp" + +using namespace rive; + +Core* ScriptedListenerActionBase::clone() const +{ + auto cloned = new ScriptedListenerAction(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/listener_group.cpp b/src/listener_group.cpp index dec81ac..217e6a1 100644 --- a/src/listener_group.cpp +++ b/src/listener_group.cpp
@@ -191,7 +191,8 @@ _listener->performChanges( stateMachineInstance, position, - Vec2D(previousPosition->x, previousPosition->y)); + Vec2D(previousPosition->x, previousPosition->y), + pointerId); stateMachineInstance->markNeedsAdvance(); consume(); } @@ -206,7 +207,8 @@ _listener->performChanges( stateMachineInstance, position, - Vec2D(previousPosition->x, previousPosition->y)); + Vec2D(previousPosition->x, previousPosition->y), + pointerId); stateMachineInstance->markNeedsAdvance(); consume(); } @@ -221,7 +223,8 @@ _listener->performChanges( stateMachineInstance, position, - Vec2D(previousPosition->x, previousPosition->y)); + Vec2D(previousPosition->x, previousPosition->y), + pointerId); stateMachineInstance->markNeedsAdvance(); if (!m_hasDragged) {
diff --git a/src/lua/lua_state.cpp b/src/lua/lua_state.cpp index 0327364..bfdd7cb 100644 --- a/src/lua/lua_state.cpp +++ b/src/lua/lua_state.cpp
@@ -14,7 +14,14 @@ ViewModel* viewModel = (ViewModel*)lua_touserdata(L, lua_upvalueindex(1)); if (viewModel) { + +#ifdef WITH_RIVE_TOOLS + viewModel->file()->triggerViewModelCreatedCallback(true); +#endif auto instance = viewModel->createInstance(); +#ifdef WITH_RIVE_TOOLS + viewModel->file()->triggerViewModelCreatedCallback(false); +#endif lua_newrive<ScriptedViewModel>(L, L, ref_rcp(viewModel), instance); return 1; }
diff --git a/tests/unit_tests/assets/scripted_listener_action.riv b/tests/unit_tests/assets/scripted_listener_action.riv new file mode 100644 index 0000000..bdb08d8 --- /dev/null +++ b/tests/unit_tests/assets/scripted_listener_action.riv Binary files differ
diff --git a/tests/unit_tests/runtime/scripting/scripting_listener_action_test.cpp b/tests/unit_tests/runtime/scripting/scripting_listener_action_test.cpp new file mode 100644 index 0000000..38889b8 --- /dev/null +++ b/tests/unit_tests/runtime/scripting/scripting_listener_action_test.cpp
@@ -0,0 +1,46 @@ + +#include "catch.hpp" +#include "scripting_test_utilities.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/lua/rive_lua_libs.hpp" +#include "rive/viewmodel/viewmodel_instance_string.hpp" +#include "rive_file_reader.hpp" + +using namespace rive; + +TEST_CASE("scripted listener action", "[silver]") +{ + rive::SerializingFactory silver; + auto file = ReadRiveFile("assets/scripted_listener_action.riv", &silver); + auto artboard = file->artboardDefault(); + + silver.frameSize(artboard->width(), artboard->height()); + REQUIRE(artboard != nullptr); + auto stateMachine = artboard->stateMachineAt(0); + + auto vmi = file->createViewModelInstance(artboard.get()); + stateMachine->bindViewModelInstance(vmi); + stateMachine->advanceAndApply(0.1f); + + auto renderer = silver.makeRenderer(); + artboard->draw(renderer.get()); + + silver.addFrame(); + + stateMachine->pointerDown(rive::Vec2D(200.0f, 20.0f), 1); + stateMachine->pointerUp(rive::Vec2D(200.0f, 20.0f), 1); + stateMachine->advanceAndApply(0.016f); + artboard->draw(renderer.get()); + + stateMachine->pointerDown(rive::Vec2D(300.0f, 20.0f), 2); + stateMachine->pointerUp(rive::Vec2D(300.0f, 20.0f), 2); + stateMachine->advanceAndApply(0.016f); + artboard->draw(renderer.get()); + + stateMachine->pointerDown(rive::Vec2D(400.0f, 20.0f), 3); + stateMachine->pointerUp(rive::Vec2D(400.0f, 20.0f), 3); + stateMachine->advanceAndApply(0.016f); + artboard->draw(renderer.get()); + + CHECK(silver.matches("scripted_listener_action")); +} \ No newline at end of file
diff --git a/tests/unit_tests/silvers/scripted_listener_action.sriv b/tests/unit_tests/silvers/scripted_listener_action.sriv new file mode 100644 index 0000000..961305a --- /dev/null +++ b/tests/unit_tests/silvers/scripted_listener_action.sriv Binary files differ