Add support to Listeners for events from nested artboards - Propagate events up to parent Artboard state machines - Add Event to Listener types and display in combobox - Add support for StateMachineListeners to listen for events Still needs implementation on CPP runtime. In this example, the rectangles are in a nested artboard. Clicking them fires an event from the nested artboard up to the parent artboard which has a listener which updates an input triggering the animation. https://github.com/rive-app/rive/assets/186340/22bfb00e-8d1e-46f0-8faa-d2d5e5a1bbfb Diffs= 1a9dd7e97 Add support to Listeners for events from nested artboards (#5923) Co-authored-by: Philip Chung <philterdesign@gmail.com>
diff --git a/.rive_head b/.rive_head index 413d351..fe1cfcf 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -52a1a6f8888c9134de8dce0393dd3b0d6757f7de +1a9dd7e97e623eb30dbec164ae0935b2ad848c1b
diff --git a/dev/defs/animation/state_machine_listener.json b/dev/defs/animation/state_machine_listener.json index 12a6892..4e99535 100644 --- a/dev/defs/animation/state_machine_listener.json +++ b/dev/defs/animation/state_machine_listener.json
@@ -15,7 +15,7 @@ "int": 224, "string": "targetid" }, - "description": "Identifier used to track the object use as a target fo this listener." + "description": "Identifier used to track the object use as a target for this listener." }, "listenerTypeValue": { "type": "uint", @@ -25,6 +25,17 @@ "string": "listenertypevalue" }, "description": "Listener type (hover, click, etc)." + }, + "eventId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 399, + "string": "eventid" + }, + "description": "Event id for the associated event, if listenerType is event" } } } \ 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 cb1d7d8..069d1a7 100644 --- a/include/rive/animation/state_machine_instance.hpp +++ b/include/rive/animation/state_machine_instance.hpp
@@ -51,6 +51,7 @@ template <typename SMType, typename InstType> InstType* getNamedInput(const std::string& name) const; + void notifyEventListeners(std::vector<EventReport> events, NestedArtboard* source); public: StateMachineInstance(const StateMachine* machine, ArtboardInstance* instance); @@ -99,6 +100,12 @@ /// the backing artboard (explicitly not allowed on Scenes). Artboard* artboard() { return m_artboardInstance; } + void setParentStateMachineInstance(StateMachineInstance* instance) { m_parentStateMachineInstance = instance; } + StateMachineInstance* parentStateMachineInstance() { return m_parentStateMachineInstance; } + + void setParentNestedArtboard(NestedArtboard* artboard) { m_parentNestedArtboard = artboard; } + NestedArtboard* parentNestedArtboard() { return m_parentNestedArtboard; } + /// Tracks an event that reported, will be cleared at the end of the next advance. void reportEvent(Event* event, float secondsDelay = 0.0f); @@ -123,6 +130,8 @@ StateMachineLayerInstance* m_layers; std::vector<std::unique_ptr<HitShape>> m_hitShapes; std::vector<NestedArtboard*> m_hitNestedArtboards; + StateMachineInstance* m_parentStateMachineInstance = nullptr; + NestedArtboard* m_parentNestedArtboard = nullptr; }; } // namespace rive #endif
diff --git a/include/rive/generated/animation/state_machine_listener_base.hpp b/include/rive/generated/animation/state_machine_listener_base.hpp index d4373f4..d02347b 100644 --- a/include/rive/generated/animation/state_machine_listener_base.hpp +++ b/include/rive/generated/animation/state_machine_listener_base.hpp
@@ -30,10 +30,12 @@ static const uint16_t targetIdPropertyKey = 224; static const uint16_t listenerTypeValuePropertyKey = 225; + static const uint16_t eventIdPropertyKey = 399; private: uint32_t m_TargetId = 0; uint32_t m_ListenerTypeValue = 0; + uint32_t m_EventId = -1; public: inline uint32_t targetId() const { return m_TargetId; } @@ -58,11 +60,23 @@ listenerTypeValueChanged(); } + inline uint32_t eventId() const { return m_EventId; } + void eventId(uint32_t value) + { + if (m_EventId == value) + { + return; + } + m_EventId = value; + eventIdChanged(); + } + Core* clone() const override; void copy(const StateMachineListenerBase& object) { m_TargetId = object.m_TargetId; m_ListenerTypeValue = object.m_ListenerTypeValue; + m_EventId = object.m_EventId; StateMachineComponent::copy(object); } @@ -76,6 +90,9 @@ case listenerTypeValuePropertyKey: m_ListenerTypeValue = CoreUintType::deserialize(reader); return true; + case eventIdPropertyKey: + m_EventId = CoreUintType::deserialize(reader); + return true; } return StateMachineComponent::deserialize(propertyKey, reader); } @@ -83,6 +100,7 @@ protected: virtual void targetIdChanged() {} virtual void listenerTypeValueChanged() {} + virtual void eventIdChanged() {} }; } // namespace rive
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index 78cdee6..5ca3da9 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp
@@ -482,6 +482,9 @@ case StateMachineListenerBase::listenerTypeValuePropertyKey: object->as<StateMachineListenerBase>()->listenerTypeValue(value); break; + case StateMachineListenerBase::eventIdPropertyKey: + object->as<StateMachineListenerBase>()->eventId(value); + break; case KeyFrameBase::framePropertyKey: object->as<KeyFrameBase>()->frame(value); break; @@ -1243,6 +1246,8 @@ return object->as<StateMachineListenerBase>()->targetId(); case StateMachineListenerBase::listenerTypeValuePropertyKey: return object->as<StateMachineListenerBase>()->listenerTypeValue(); + case StateMachineListenerBase::eventIdPropertyKey: + return object->as<StateMachineListenerBase>()->eventId(); case KeyFrameBase::framePropertyKey: return object->as<KeyFrameBase>()->frame(); case InterpolatingKeyFrameBase::interpolationTypePropertyKey: @@ -1741,6 +1746,7 @@ case KeyedPropertyBase::propertyKeyPropertyKey: case StateMachineListenerBase::targetIdPropertyKey: case StateMachineListenerBase::listenerTypeValuePropertyKey: + case StateMachineListenerBase::eventIdPropertyKey: case KeyFrameBase::framePropertyKey: case InterpolatingKeyFrameBase::interpolationTypePropertyKey: case InterpolatingKeyFrameBase::interpolatorIdPropertyKey:
diff --git a/include/rive/listener_type.hpp b/include/rive/listener_type.hpp index f128b65..d79f68c 100644 --- a/include/rive/listener_type.hpp +++ b/include/rive/listener_type.hpp
@@ -9,6 +9,7 @@ down = 2, up = 3, move = 4, + event = 5, }; } #endif \ No newline at end of file
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp index 54c337b..eeb9562 100644 --- a/src/animation/state_machine_instance.cpp +++ b/src/animation/state_machine_instance.cpp
@@ -408,6 +408,7 @@ break; case ListenerType::enter: case ListenerType::exit: + case ListenerType::event: break; } } @@ -509,6 +510,18 @@ if (nestedArtboard->hasNestedStateMachines()) { m_hitNestedArtboards.push_back(nestedArtboard); + for (auto animation : nestedArtboard->nestedAnimations()) + { + if (animation->is<NestedStateMachine>()) + { + animation->as<NestedStateMachine>() + ->stateMachineInstance() + ->setParentNestedArtboard(nestedArtboard); + animation->as<NestedStateMachine>() + ->stateMachineInstance() + ->setParentStateMachineInstance(this); + } + } } } } @@ -526,6 +539,7 @@ bool StateMachineInstance::advance(float seconds) { + this->notifyEventListeners(m_reportedEvents, nullptr); m_reportedEvents.clear(); m_needsAdvance = false; for (size_t i = 0; i < m_layerCount; i++) @@ -675,4 +689,37 @@ auto coreObject = m_artboardInstance->resolve(objectId); CallbackData data(this, elapsedSeconds); CoreRegistry::setCallback(coreObject, propertyKey, data); +} + +void StateMachineInstance::notifyEventListeners(std::vector<EventReport> events, NestedArtboard* source) +{ + if (events.size() > 0) + { + // We trigger the listeners in order + for (size_t i = 0; i < m_machine->listenerCount(); i++) + { + auto listener = m_machine->listener(i); + auto target = artboard()->resolve(listener->targetId()); + if (listener != nullptr && + listener->listenerType() == ListenerType::event && + (source == nullptr || source == target)) + { + for (const auto event : events) + { + auto sourceArtboard = source == nullptr ? artboard() : source->artboard(); + auto listenerEvent = sourceArtboard->resolve(listener->eventId()); + if (listenerEvent == event.event()) + { + listener->performChanges(this, Vec2D()); + break; + } + } + } + } + // Bubble the event up to parent artboard state machines immediately + if (m_parentStateMachineInstance != nullptr) + { + m_parentStateMachineInstance->notifyEventListeners(events, m_parentNestedArtboard); + } + } } \ No newline at end of file