blob: 9bf1360d52f2eb5ee0a9e200ff67b39aa592a451 [file]
#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 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;
// 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