| #ifndef _RIVE_STATE_MACHINE_INSTANCE_CLUSTERS_HPP_ |
| #define _RIVE_STATE_MACHINE_INSTANCE_CLUSTERS_HPP_ |
| |
| // Implementation detail of StateMachineInstance. Only the translation units |
| // that define StateMachineInstance members need it — state_machine_instance.cpp |
| // and src/input/gamepad_batch.cpp — and state_machine_instance.hpp forward |
| // declares these types rather than including this, so its heavier includes |
| // (listener groups, SemanticManager, GamepadSnapshot) stay out of the public |
| // surface. Do not include it just to reach a state machine; everything callers |
| // need is on StateMachineInstance itself. |
| // |
| // StateMachineInstance is built once per row of an ArtboardComponentList, so |
| // every inline byte is multiplied by the row count. The clusters below hold the |
| // state that is untouched on a plain state machine — events, bindable-property |
| // instancing, focus / keyboard / gamepad / semantics, and scripting — and hang |
| // off the instance through a `Sidecar` (8 B, null until the feature is |
| // actually used). Read paths null-check the sidecar, which is strictly cheaper |
| // than the per-container `.empty()` probes it replaces; write paths call the |
| // matching `ensure...()` on StateMachineInstance. |
| |
| #include "rive/animation/gamepad_listener_group.hpp" |
| #include "rive/animation/keyboard_listener_group.hpp" |
| #include "rive/animation/semantic_listener_group.hpp" |
| #include "rive/event_report.hpp" |
| #include "rive/input/gamepad_snapshot.hpp" |
| #include "rive/semantic/semantic_manager.hpp" |
| |
| #include <memory> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| namespace rive |
| { |
| class BindableProperty; |
| class BindablePropertyNumber; |
| class Core; |
| class DataBind; |
| class FocusListenerGroup; |
| class ListenerViewModel; |
| class ScriptedDrawable; |
| class ScriptedObject; |
| |
| /// Event and viewModel-listener reporting. Allocated the first time the file |
| /// reports an event or registers a viewModel listener. |
| struct SMIReporting |
| { |
| /// Events reported since the last applyEvents, awaiting delivery. |
| std::vector<EventReport> reportedEvents; |
| /// The batch currently being delivered. The reported/reporting split is |
| /// load-bearing for re-entrancy, not just capacity caching: a listener |
| /// action can re-enter reportEvent() while this batch is being notified, |
| /// and those new events must land in `reportedEvents` for the next pass. |
| std::vector<EventReport> reportingEvents; |
| /// Events first reported inside the applyEvents loop; kept host visible |
| /// until the next applyEvents so hosts see them exactly once. |
| std::vector<EventReport> eventsAppliedDuringLoop; |
| /// Owned, one per viewModel listener declared by the state machine. |
| std::vector<ListenerViewModel*> listenerViewModels; |
| std::vector<ListenerViewModel*> reportedListenerViewModels; |
| std::vector<ListenerViewModel*> reportingListenerViewModels; |
| }; |
| |
| /// Per-instance clones of the BindableProperty / StateTransition values a data |
| /// bind writes to, so instances never write through to shared file data. |
| /// Allocated only when a state machine data bind targets one of them. |
| struct SMIBindables |
| { |
| /// Shared BindableProperty -> this instance's owned clone. |
| std::unordered_map<BindableProperty*, BindableProperty*> propertyInstances; |
| std::unordered_map<BindableProperty*, DataBind*> dataBindsToTarget; |
| std::unordered_map<BindableProperty*, DataBind*> dataBindsToSource; |
| /// Map from shared StateTransition* to per-instance BindablePropertyNumber |
| /// instances, keyed by original property key. Data binds write to these |
| /// instead of the shared StateTransition object. |
| std::unordered_map<const Core*, |
| std::unordered_map<uint32_t, BindablePropertyNumber*>> |
| transitionPropertyInstances; |
| }; |
| |
| struct QueuedFocusEvent |
| { |
| FocusListenerGroup* group; |
| bool isFocus; |
| }; |
| |
| struct QueuedSemanticEvent |
| { |
| SemanticListenerGroup* group; |
| SemanticActionType actionType; |
| }; |
| |
| /// Everything driven by focus, keyboard, gamepad, or accessibility rather than |
| /// by pointer events. Allocated when the file declares a focus/blur, keyboard, |
| /// textInput, gamepad, or semanticAction listener, when a script wants keyboard |
| /// or gamepad input, or when enableSemantics() is called. |
| struct SMIInputExtras |
| { |
| std::vector<std::unique_ptr<FocusListenerGroup>> focusListenerGroups; |
| std::vector<std::unique_ptr<KeyboardListenerGroup>> keyboardListenerGroups; |
| std::vector<std::unique_ptr<GamepadListenerGroup>> gamepadListenerGroups; |
| std::vector<std::unique_ptr<SemanticListenerGroup>> semanticListenerGroups; |
| /// Non-owning back-references to every `ScriptedDrawable` whose script |
| /// declares a gamepad handler. Populated once at init from the artboard |
| /// (which outlives this state machine) and walked by |
| /// `broadcastGamepadToScriptedDrawables` so events reach scripts that are |
| /// not on the focus chain. Mirrors how `m_hitComponents` lets every |
| /// pointer-aware drawable react regardless of focus. |
| std::vector<ScriptedDrawable*> gamepadScriptedDrawables; |
| /// Latest embedder gamepad state for `submitGamepadsFromBuffer` (WASM/JS). |
| std::unordered_map<int, GamepadSnapshot> embedderGamepads; |
| |
| std::unique_ptr<SemanticManager> semanticManager; |
| SemanticManager* externalSemanticManager = nullptr; |
| |
| /// Queued for deferred execution during advance(). |
| std::vector<QueuedFocusEvent> queuedFocusEvents; |
| std::vector<QueuedSemanticEvent> queuedSemanticEvents; |
| }; |
| |
| /// Per-instance clones of the state machine's scripted objects. Allocated only |
| /// when the file uses scripting. |
| struct SMIScripting |
| { |
| /// (shared source, owned instance) pairs in the state machine's authored |
| /// order. A vector rather than a map so Lua `init` runs in a deterministic |
| /// order; lookups are a handful of entries and are always followed by a VM |
| /// call that dwarfs the scan. |
| std::vector<std::pair<const ScriptedObject*, ScriptedObject*>> objects; |
| |
| ScriptedObject* find(const ScriptedObject* source) const |
| { |
| for (const auto& pair : objects) |
| { |
| if (pair.first == source) |
| { |
| return pair.second; |
| } |
| } |
| return nullptr; |
| } |
| }; |
| |
| } // namespace rive |
| #endif |