feature: scripted listener actions (#11468) f3a89390cb

Co-authored-by: hernan <hernan@rive.app>
This commit is contained in:
bodymovin
2026-01-15 19:53:42 +00:00
parent bfe1ebbcf2
commit 6eaf71f485
32 changed files with 389 additions and 25 deletions

View File

@@ -1 +1 @@
9112280455e25db4649d446601f443c763151649 f3a89390cb428a5ea841d21de91f9cb2adc312df

View File

@@ -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"
}
}
}
}

View File

@@ -12,7 +12,8 @@ public:
StatusCode import(ImportStack& importStack) override; StatusCode import(ImportStack& importStack) override;
virtual void perform(StateMachineInstance* stateMachineInstance, virtual void perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const = 0; Vec2D previousPosition,
int pointerId) const = 0;
}; };
} // namespace rive } // namespace rive

View File

@@ -9,7 +9,8 @@ class ListenerAlignTarget : public ListenerAlignTargetBase
public: public:
void perform(StateMachineInstance* stateMachineInstance, void perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const override; Vec2D previousPosition,
int pointerId) const override;
}; };
} // namespace rive } // namespace rive

View File

@@ -12,7 +12,8 @@ public:
bool validateNestedInputType(const NestedInput* input) const override; bool validateNestedInputType(const NestedInput* input) const override;
void perform(StateMachineInstance* stateMachineInstance, void perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const override; Vec2D previousPosition,
int pointerId) const override;
}; };
} // namespace rive } // namespace rive

View File

@@ -9,7 +9,8 @@ class ListenerFireEvent : public ListenerFireEventBase
public: public:
void perform(StateMachineInstance* stateMachineInstance, void perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const override; Vec2D previousPosition,
int pointerId) const override;
}; };
} // namespace rive } // namespace rive

View File

@@ -12,7 +12,8 @@ public:
bool validateNestedInputType(const NestedInput* input) const override; bool validateNestedInputType(const NestedInput* input) const override;
void perform(StateMachineInstance* stateMachineInstance, void perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const override; Vec2D previousPosition,
int pointerId) const override;
}; };
} // namespace rive } // namespace rive

View File

@@ -12,7 +12,8 @@ public:
bool validateNestedInputType(const NestedInput* input) const override; bool validateNestedInputType(const NestedInput* input) const override;
void perform(StateMachineInstance* stateMachineInstance, void perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const override; Vec2D previousPosition,
int pointerId) const override;
}; };
} // namespace rive } // namespace rive

View File

@@ -11,7 +11,8 @@ public:
~ListenerViewModelChange(); ~ListenerViewModelChange();
void perform(StateMachineInstance* stateMachineInstance, void perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const override; Vec2D previousPosition,
int pointerId) const override;
StatusCode import(ImportStack& importStack) override; StatusCode import(ImportStack& importStack) override;
private: private:

View File

@@ -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

View File

@@ -40,6 +40,7 @@ class DataBind;
class BindableProperty; class BindableProperty;
class HitDrawable; class HitDrawable;
class ListenerViewModel; class ListenerViewModel;
class ScriptedListenerAction;
typedef void (*DataBindChanged)(); typedef void (*DataBindChanged)();
#ifdef WITH_RIVE_TOOLS #ifdef WITH_RIVE_TOOLS
@@ -205,6 +206,7 @@ public:
bool hasListeners() { return m_hitComponents.size() > 0; } bool hasListeners() { return m_hitComponents.size() > 0; }
void clearDataContext(); void clearDataContext();
void internalDataContext(DataContext* dataContext); void internalDataContext(DataContext* dataContext);
ScriptedObject* scriptedObject(const ScriptedObject*);
#ifdef TESTING #ifdef TESTING
size_t hitComponentsCount() { return m_hitComponents.size(); }; size_t hitComponentsCount() { return m_hitComponents.size(); };
HitComponent* hitComponent(size_t index) HitComponent* hitComponent(size_t index)
@@ -238,6 +240,8 @@ private:
std::vector<ListenerViewModel*> m_reportingListenerViewModels; std::vector<ListenerViewModel*> m_reportingListenerViewModels;
std::unordered_map<BindableProperty*, BindableProperty*> std::unordered_map<BindableProperty*, BindableProperty*>
m_bindablePropertyInstances; m_bindablePropertyInstances;
std::unordered_map<const ScriptedObject*, ScriptedObject*>
m_scriptedListenerActionsMap;
std::unordered_map<BindableProperty*, DataBind*> std::unordered_map<BindableProperty*, DataBind*>
m_bindableDataBindsToTarget; m_bindableDataBindsToTarget;
std::unordered_map<BindableProperty*, DataBind*> std::unordered_map<BindableProperty*, DataBind*>

View File

@@ -31,7 +31,8 @@ public:
void performChanges(StateMachineInstance* stateMachineInstance, void performChanges(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const; Vec2D previousPosition,
int pointerId) const;
void decodeViewModelPathIds(Span<const uint8_t> value) override; void decodeViewModelPathIds(Span<const uint8_t> value) override;
void copyViewModelPathIds(const StateMachineListenerBase& object) override; void copyViewModelPathIds(const StateMachineListenerBase& object) override;
std::vector<uint32_t> viewModelPathIdsBuffer() const; std::vector<uint32_t> viewModelPathIdsBuffer() const;

View File

@@ -25,7 +25,8 @@ enum ScriptProtocol
node, node,
layout, layout,
converter, converter,
pathEffect pathEffect,
listenerAction
}; };
#ifdef WITH_RIVE_SCRIPTING #ifdef WITH_RIVE_SCRIPTING

View File

@@ -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

View File

@@ -50,6 +50,7 @@
#include "rive/animation/nested_simple_animation.hpp" #include "rive/animation/nested_simple_animation.hpp"
#include "rive/animation/nested_state_machine.hpp" #include "rive/animation/nested_state_machine.hpp"
#include "rive/animation/nested_trigger.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.hpp"
#include "rive/animation/state_machine_bool.hpp" #include "rive/animation/state_machine_bool.hpp"
#include "rive/animation/state_machine_component.hpp" #include "rive/animation/state_machine_component.hpp"
@@ -477,6 +478,8 @@ public:
return new AnimationState(); return new AnimationState();
case NestedTriggerBase::typeKey: case NestedTriggerBase::typeKey:
return new NestedTrigger(); return new NestedTrigger();
case ScriptedListenerActionBase::typeKey:
return new ScriptedListenerAction();
case KeyedObjectBase::typeKey: case KeyedObjectBase::typeKey:
return new KeyedObject(); return new KeyedObject();
case AnimationBase::typeKey: case AnimationBase::typeKey:
@@ -1194,6 +1197,9 @@ public:
case NestedInputBase::inputIdPropertyKey: case NestedInputBase::inputIdPropertyKey:
object->as<NestedInputBase>()->inputId(value); object->as<NestedInputBase>()->inputId(value);
break; break;
case ScriptedListenerActionBase::scriptAssetIdPropertyKey:
object->as<ScriptedListenerActionBase>()->scriptAssetId(value);
break;
case KeyedObjectBase::objectIdPropertyKey: case KeyedObjectBase::objectIdPropertyKey:
object->as<KeyedObjectBase>()->objectId(value); object->as<KeyedObjectBase>()->objectId(value);
break; break;
@@ -2737,6 +2743,9 @@ public:
return object->as<AnimationStateBase>()->animationId(); return object->as<AnimationStateBase>()->animationId();
case NestedInputBase::inputIdPropertyKey: case NestedInputBase::inputIdPropertyKey:
return object->as<NestedInputBase>()->inputId(); return object->as<NestedInputBase>()->inputId();
case ScriptedListenerActionBase::scriptAssetIdPropertyKey:
return object->as<ScriptedListenerActionBase>()
->scriptAssetId();
case KeyedObjectBase::objectIdPropertyKey: case KeyedObjectBase::objectIdPropertyKey:
return object->as<KeyedObjectBase>()->objectId(); return object->as<KeyedObjectBase>()->objectId();
case BlendAnimationBase::animationIdPropertyKey: case BlendAnimationBase::animationIdPropertyKey:
@@ -3725,6 +3734,7 @@ public:
case ListenerInputChangeBase::nestedInputIdPropertyKey: case ListenerInputChangeBase::nestedInputIdPropertyKey:
case AnimationStateBase::animationIdPropertyKey: case AnimationStateBase::animationIdPropertyKey:
case NestedInputBase::inputIdPropertyKey: case NestedInputBase::inputIdPropertyKey:
case ScriptedListenerActionBase::scriptAssetIdPropertyKey:
case KeyedObjectBase::objectIdPropertyKey: case KeyedObjectBase::objectIdPropertyKey:
case BlendAnimationBase::animationIdPropertyKey: case BlendAnimationBase::animationIdPropertyKey:
case BlendAnimationDirectBase::inputIdPropertyKey: case BlendAnimationDirectBase::inputIdPropertyKey:
@@ -4377,6 +4387,8 @@ public:
return object->is<AnimationStateBase>(); return object->is<AnimationStateBase>();
case NestedInputBase::inputIdPropertyKey: case NestedInputBase::inputIdPropertyKey:
return object->is<NestedInputBase>(); return object->is<NestedInputBase>();
case ScriptedListenerActionBase::scriptAssetIdPropertyKey:
return object->is<ScriptedListenerActionBase>();
case KeyedObjectBase::objectIdPropertyKey: case KeyedObjectBase::objectIdPropertyKey:
return object->is<KeyedObjectBase>(); return object->is<KeyedObjectBase>();
case BlendAnimationBase::animationIdPropertyKey: case BlendAnimationBase::animationIdPropertyKey:

View File

@@ -33,6 +33,8 @@ protected:
#ifdef WITH_RIVE_TOOLS #ifdef WITH_RIVE_TOOLS
bool hasValidVM(); bool hasValidVM();
#endif #endif
private:
DataContext* m_dataContext = nullptr;
public: public:
virtual ~ScriptedObject() { scriptDispose(); } virtual ~ScriptedObject() { scriptDispose(); }
@@ -48,7 +50,8 @@ public:
void scriptUpdate(); void scriptUpdate();
void reinit(); void reinit();
virtual void markNeedsUpdate(); 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 #ifdef WITH_RIVE_SCRIPTING
virtual bool scriptInit(lua_State* state); virtual bool scriptInit(lua_State* state);
lua_State* state() { return m_state; } lua_State* state() { return m_state; }

View File

@@ -7,7 +7,8 @@ using namespace rive;
void ListenerAlignTarget::perform(StateMachineInstance* stateMachineInstance, void ListenerAlignTarget::perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const Vec2D previousPosition,
int pointerId) const
{ {
auto coreTarget = stateMachineInstance->artboard()->resolve(targetId()); auto coreTarget = stateMachineInstance->artboard()->resolve(targetId());
if (coreTarget == nullptr || !coreTarget->is<Node>()) if (coreTarget == nullptr || !coreTarget->is<Node>())

View File

@@ -26,7 +26,8 @@ bool ListenerBoolChange::validateNestedInputType(const NestedInput* input) const
void ListenerBoolChange::perform(StateMachineInstance* stateMachineInstance, void ListenerBoolChange::perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const Vec2D previousPosition,
int pointerId) const
{ {
if (nestedInputId() != Core::emptyId) if (nestedInputId() != Core::emptyId)
{ {

View File

@@ -6,7 +6,8 @@ using namespace rive;
void ListenerFireEvent::perform(StateMachineInstance* stateMachineInstance, void ListenerFireEvent::perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const Vec2D previousPosition,
int pointerId) const
{ {
auto coreEvent = stateMachineInstance->artboard()->resolve(eventId()); auto coreEvent = stateMachineInstance->artboard()->resolve(eventId());
if (coreEvent == nullptr || !coreEvent->is<Event>()) if (coreEvent == nullptr || !coreEvent->is<Event>())

View File

@@ -29,7 +29,8 @@ bool ListenerNumberChange::validateNestedInputType(
void ListenerNumberChange::perform(StateMachineInstance* stateMachineInstance, void ListenerNumberChange::perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const Vec2D previousPosition,
int pointerId) const
{ {
if (nestedInputId() != Core::emptyId) if (nestedInputId() != Core::emptyId)
{ {

View File

@@ -30,7 +30,8 @@ bool ListenerTriggerChange::validateNestedInputType(
void ListenerTriggerChange::perform(StateMachineInstance* stateMachineInstance, void ListenerTriggerChange::perform(StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const Vec2D previousPosition,
int pointerId) const
{ {
if (nestedInputId() != Core::emptyId) if (nestedInputId() != Core::emptyId)
{ {

View File

@@ -37,7 +37,8 @@ StatusCode ListenerViewModelChange::import(ImportStack& importStack)
void ListenerViewModelChange::perform( void ListenerViewModelChange::perform(
StateMachineInstance* stateMachineInstance, StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const Vec2D previousPosition,
int pointerId) const
{ {
// Get the bindable property instance from the state machine instance // Get the bindable property instance from the state machine instance
// context // context

View File

@@ -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;
}

View File

@@ -19,6 +19,8 @@
#include "rive/animation/state_machine_trigger.hpp" #include "rive/animation/state_machine_trigger.hpp"
#include "rive/animation/state_machine.hpp" #include "rive/animation/state_machine.hpp"
#include "rive/animation/state_transition.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_condition.hpp"
#include "rive/animation/transition_comparator.hpp" #include "rive/animation/transition_comparator.hpp"
#include "rive/animation/transition_property_viewmodel_comparator.hpp" #include "rive/animation/transition_property_viewmodel_comparator.hpp"
@@ -1480,9 +1482,41 @@ StateMachineInstance::StateMachineInstance(const StateMachine* machine,
this); this);
m_hitComponents.push_back(std::move(hc)); 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(); 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() StateMachineInstance::~StateMachineInstance()
{ {
unbind(); unbind();
@@ -1506,6 +1540,12 @@ StateMachineInstance::~StateMachineInstance()
delete listenerViewModel; delete listenerViewModel;
} }
m_bindablePropertyInstances.clear(); m_bindablePropertyInstances.clear();
for (auto& pair : m_scriptedListenerActionsMap)
{
delete pair.second;
pair.second = nullptr;
}
m_scriptedListenerActionsMap.clear();
} }
void StateMachineInstance::removeEventListeners() void StateMachineInstance::removeEventListeners()
@@ -1816,6 +1856,10 @@ void StateMachineInstance::internalDataContext(DataContext* dataContext)
{ {
listenerViewModel->bindFromContext(dataContext); listenerViewModel->bindFromContext(dataContext);
} }
for (auto& scriptedObjectItr : m_scriptedListenerActionsMap)
{
scriptedObjectItr.second->dataContext(dataContext);
}
} }
void StateMachineInstance::rebind() void StateMachineInstance::rebind()
@@ -1955,7 +1999,8 @@ void StateMachineInstance::notifyListenerViewModels(
{ {
listenerViewModel->listener()->performChanges(this, listenerViewModel->listener()->performChanges(this,
Vec2D(), Vec2D(),
Vec2D()); Vec2D(),
0);
} }
} }
} }
@@ -2011,7 +2056,7 @@ void StateMachineInstance::notifyEventListeners(
sourceArtboard->resolve(listener->eventId()); sourceArtboard->resolve(listener->eventId());
if (listenerEvent == event.event()) if (listenerEvent == event.event())
{ {
listener->performChanges(this, Vec2D(), Vec2D()); listener->performChanges(this, Vec2D(), Vec2D(), 0);
break; break;
} }
} }

View File

@@ -44,11 +44,15 @@ const ListenerAction* StateMachineListener::action(size_t index) const
void StateMachineListener::performChanges( void StateMachineListener::performChanges(
StateMachineInstance* stateMachineInstance, StateMachineInstance* stateMachineInstance,
Vec2D position, Vec2D position,
Vec2D previousPosition) const Vec2D previousPosition,
int pointerId) const
{ {
for (auto& action : m_actions) for (auto& action : m_actions)
{ {
action->perform(stateMachineInstance, position, previousPosition); action->perform(stateMachineInstance,
position,
previousPosition,
pointerId);
} }
} }

View File

@@ -66,7 +66,8 @@ bool OptionalScriptedMethods::verifyImplementation(ScriptedObject* object,
if (scriptProtocol == ScriptProtocol::node || if (scriptProtocol == ScriptProtocol::node ||
scriptProtocol == ScriptProtocol::layout || scriptProtocol == ScriptProtocol::layout ||
scriptProtocol == ScriptProtocol::converter || scriptProtocol == ScriptProtocol::converter ||
scriptProtocol == ScriptProtocol::pathEffect) scriptProtocol == ScriptProtocol::pathEffect ||
scriptProtocol == ScriptProtocol::listenerAction)
{ {
if (static_cast<lua_Type>(lua_getfield(state, -1, "update")) == if (static_cast<lua_Type>(lua_getfield(state, -1, "update")) ==
LUA_TFUNCTION) LUA_TFUNCTION)

View File

@@ -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;
}

View File

@@ -191,7 +191,8 @@ ProcessEventResult ListenerGroup::processEvent(
_listener->performChanges( _listener->performChanges(
stateMachineInstance, stateMachineInstance,
position, position,
Vec2D(previousPosition->x, previousPosition->y)); Vec2D(previousPosition->x, previousPosition->y),
pointerId);
stateMachineInstance->markNeedsAdvance(); stateMachineInstance->markNeedsAdvance();
consume(); consume();
} }
@@ -206,7 +207,8 @@ ProcessEventResult ListenerGroup::processEvent(
_listener->performChanges( _listener->performChanges(
stateMachineInstance, stateMachineInstance,
position, position,
Vec2D(previousPosition->x, previousPosition->y)); Vec2D(previousPosition->x, previousPosition->y),
pointerId);
stateMachineInstance->markNeedsAdvance(); stateMachineInstance->markNeedsAdvance();
consume(); consume();
} }
@@ -221,7 +223,8 @@ ProcessEventResult ListenerGroup::processEvent(
_listener->performChanges( _listener->performChanges(
stateMachineInstance, stateMachineInstance,
position, position,
Vec2D(previousPosition->x, previousPosition->y)); Vec2D(previousPosition->x, previousPosition->y),
pointerId);
stateMachineInstance->markNeedsAdvance(); stateMachineInstance->markNeedsAdvance();
if (!m_hasDragged) if (!m_hasDragged)
{ {

View File

@@ -14,7 +14,14 @@ static int viewmodel_new(lua_State* L)
ViewModel* viewModel = (ViewModel*)lua_touserdata(L, lua_upvalueindex(1)); ViewModel* viewModel = (ViewModel*)lua_touserdata(L, lua_upvalueindex(1));
if (viewModel) if (viewModel)
{ {
#ifdef WITH_RIVE_TOOLS
viewModel->file()->triggerViewModelCreatedCallback(true);
#endif
auto instance = viewModel->createInstance(); auto instance = viewModel->createInstance();
#ifdef WITH_RIVE_TOOLS
viewModel->file()->triggerViewModelCreatedCallback(false);
#endif
lua_newrive<ScriptedViewModel>(L, L, ref_rcp(viewModel), instance); lua_newrive<ScriptedViewModel>(L, L, ref_rcp(viewModel), instance);
return 1; return 1;
} }

Binary file not shown.

View File

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

Binary file not shown.