| #ifndef _RIVE_DATA_CONTEXT_HPP_ | |
| #define _RIVE_DATA_CONTEXT_HPP_ | |
| #include "rive/viewmodel/viewmodel_instance_value.hpp" | |
| #include "rive/viewmodel/viewmodel_instance.hpp" | |
| #include "rive/data_resolver.hpp" | |
| #include "rive/refcnt.hpp" | |
| #include <cstdint> | |
| #include <limits> | |
| #include <memory> | |
| #include <vector> | |
| namespace rive | |
| { | |
| class DataBindPath; | |
| class File; | |
| class DataContext : public RefCnt<DataContext> | |
| { | |
| private: | |
| // Sentinel slot key for an entry that is not on the slot keys, i.e. a main | |
| // view model instance. A real slot key is a viewModelId (an index into the | |
| // file's view models), so this value can never be a valid slot. | |
| static constexpr uint32_t kNoSlot = std::numeric_limits<uint32_t>::max(); | |
| rcp<DataContext> m_Parent = nullptr; | |
| std::vector<rcp<ViewModelInstance>> m_ViewModelInstances; | |
| // Containers (artboard + its state machines) that share this context and | |
| // must be kept registered as dependents of every instance it holds. The | |
| // context owns this bookkeeping so that mutating a shared context re-points | |
| // ALL attached containers, not just the caller. Raw pointers: a container | |
| // deregisters via removeDependentContainer before it dies (and the | |
| // destructor detaches defensively). | |
| std::vector<DataBindContainer*> m_dependentContainers; | |
| // Add/remove every attached container as a dependent of one instance. | |
| void attachContainers(ViewModelInstance* instance); | |
| void detachContainers(ViewModelInstance* instance); | |
| // Slot bookkeeping for contexts that carry global view model instances. | |
| // A "slot key" is the declared view model id a slot represents; it lets us | |
| // place an instance in the right slot (and find/replace it) even when the | |
| // instance's own view model differs from the slot's (the "override" case). | |
| // Kept index-aligned with m_ViewModelInstances and allocated lazily, so the | |
| // many global-free contexts pay only a single null pointer. Resolution | |
| // never consults it. | |
| struct GlobalSlots | |
| { | |
| std::vector<uint32_t> slotKeys; | |
| }; | |
| std::unique_ptr<GlobalSlots> m_globalSlots; | |
| // Ensures m_globalSlots exists and its slotKeys are index-aligned with | |
| // m_ViewModelInstances, backfilling any missing entries with kNoSlot (they | |
| // were mains before the first global was slotted). | |
| void ensureGlobalSlots(); | |
| // The slot key for the instance at index i (its stored key when tracked, | |
| // otherwise kNoSlot, meaning the entry is a main / not on the slot keys). | |
| uint32_t slotKeyAt(size_t index) const; | |
| // Insert/remove an instance while keeping slot keys aligned. | |
| void insertInstanceAt(size_t index, | |
| rcp<ViewModelInstance> value, | |
| uint32_t slotKey); | |
| void removeInstanceAt(size_t index); | |
| ViewModelInstanceValue* tryGetViewModelProperty( | |
| rcp<ViewModelInstance> instance, | |
| const std::vector<uint32_t>& path) const; | |
| ViewModelInstanceValue* tryGetRelativeViewModelProperty( | |
| rcp<ViewModelInstance> instance, | |
| const std::vector<uint32_t>& path, | |
| DataResolver* resolver) const; | |
| rcp<ViewModelInstance> tryGetViewModelInstance( | |
| rcp<ViewModelInstance> instance, | |
| const std::vector<uint32_t>& path) const; | |
| rcp<ViewModelInstance> tryGetRelativeViewModelInstance( | |
| rcp<ViewModelInstance> instance, | |
| const std::vector<uint32_t>& path, | |
| DataResolver* resolver) const; | |
| public: | |
| DataContext(rcp<ViewModelInstance> viewModelInstance); | |
| DataContext(std::vector<rcp<ViewModelInstance>> viewModelInstances); | |
| ~DataContext(); | |
| // Registers a container as a dependent of this context. Idempotent; also | |
| // registers it against every instance currently held. | |
| void addDependentContainer(DataBindContainer* container); | |
| // Unregisters a container from this context and from every instance it | |
| // holds. Call before the container is destroyed. | |
| void removeDependentContainer(DataBindContainer* container); | |
| rcp<DataContext> parent() { return m_Parent; } | |
| void parent(rcp<DataContext> value) { m_Parent = value; } | |
| ViewModelInstanceValue* getViewModelProperty( | |
| const std::vector<uint32_t> path) const; | |
| ViewModelInstanceValue* getRelativeViewModelProperty( | |
| const std::vector<uint32_t> path, | |
| DataResolver* resolver) const; | |
| ViewModelInstanceValue* getViewModelProperty(DataBindPath* dataBindPath); | |
| rcp<ViewModelInstance> getViewModelInstance( | |
| const std::vector<uint32_t> path) const; | |
| rcp<ViewModelInstance> getViewModelInstance(DataBindPath*) const; | |
| rcp<ViewModelInstance> getRelativeViewModelInstance( | |
| const std::vector<uint32_t> path, | |
| DataResolver* resolver) const; | |
| void viewModelInstance(rcp<ViewModelInstance> value); | |
| // Insert-or-replace the instance occupying `slotKey`. Positions by | |
| // canonical order (non-global/main first, then ascending slotKey). The | |
| // instance's own viewModelId is irrelevant to placement, so any instance | |
| // can fill any slot. | |
| void setViewModelInstanceForSlot(uint32_t slotKey, | |
| rcp<ViewModelInstance> value); | |
| // The instance currently occupying `slotKey`, or nullptr. | |
| rcp<ViewModelInstance> instanceForSlot(uint32_t slotKey) const; | |
| // Resolves a global view model instance by name from this context, | |
| // validating through the file like Artboard::setGlobalViewModelInstance. | |
| rcp<ViewModelInstance> resolveGlobalViewModel(File* file, const char* name); | |
| // Removes the main view model instance(s): every entry not on the slot | |
| // keys. | |
| void removeMainViewModelInstance(); | |
| // Replaces the single "main" view model instance (the entry not on the slot | |
| // keys), preserving any global instances and their slot order. The result | |
| // is always ordered as [main, globals...]. | |
| void setMainViewModelInstance(rcp<ViewModelInstance> value); | |
| void advanced(); | |
| // The main view model instance: the first entry not on the slot keys, or | |
| // nullptr if the context holds only globals. | |
| rcp<ViewModelInstance> mainViewModelInstance() const; | |
| const std::vector<rcp<ViewModelInstance>>& viewModelInstances() const | |
| { | |
| return m_ViewModelInstances; | |
| } | |
| rcp<ViewModelInstance> rootViewModelInstance() | |
| { | |
| if (m_Parent) | |
| { | |
| return m_Parent->rootViewModelInstance(); | |
| } | |
| return mainViewModelInstance(); | |
| }; | |
| ViewModelInstanceValue* viewModelValue() | |
| { | |
| if (m_Parent) | |
| { | |
| return m_Parent->viewModelValue(); | |
| } | |
| return nullptr; | |
| } | |
| }; | |
| } // namespace rive | |
| #endif |