blob: d0dafd5c78dbdb681a548c2089fdc89efd4a6094 [file]
#include "rive/data_bind/converters/data_converter.hpp"
#include "rive/importers/import_stack.hpp"
#include "rive/importers/backboard_importer.hpp"
#include "rive/backboard.hpp"
#include "rive/data_bind/data_bind.hpp"
#include "rive/data_bind/data_bind_context.hpp"
using namespace rive;
DataConverter::~DataConverter() { deleteDataBinds(); }
StatusCode DataConverter::import(ImportStack& importStack)
{
auto backboardImporter = importStack.latest<BackboardImporter>(
Backboard::typeKey);
if (backboardImporter == nullptr)
{
return StatusCode::MissingObject;
}
backboardImporter->addDataConverter(this);
return Super::import(importStack);
}
void DataConverter::bindFromContext(
DataContext* dataContext,
DataBind* dataBind)
{
m_parentDataBind = dataBind;
bindDataBindsFromContext(rcp<DataContext>(safe_ref(dataContext)));
}
void DataConverter::unbind() { unbindDataBinds(); }
void DataConverter::markConverterDirty()
{
if (m_parentDataBind != nullptr)
{
// Re-assert the direction of the change currently in flight rather than
// hardcoding source→target: a target-originated interpolation must keep
// flowing target→source across the frames it advances. Dependents keeps
// the context value invalidated so the apply re-runs (and steps the
// interpolator) every frame.
m_parentDataBind->addDirt(
ComponentDirt::Dependents |
(m_parentDataBind->targetOrigin() ? ComponentDirt::BindingsTarget
: ComponentDirt::Bindings),
false);
}
}
void DataConverter::addDirtyDataBind(DataBind* dataBind)
{
markConverterDirty();
DataBindContainer::addDirtyDataBind(dataBind);
}
void DataConverter::update() { updateDataBinds(false); }
void DataConverter::copy(const DataConverter& object)
{
auto dataBinds = object.dataBinds();
for (auto dataBind : dataBinds)
{
// Binds on a converter's properties can use converters of their own, so
// each clone takes a clone of its bind's converter along.
addDataBind(dataBind->cloneWithTarget(this));
}
DataConverterBase::copy(object);
}
bool DataConverter::advance(float elapsedTime)
{
return advanceDataBinds(elapsedTime);
}
bool DataConverter::mayAdvance() const { return mayAdvanceDataBinds(); }
void DataConverter::dataBindsChanged()
{
// The binds and converters that use this converter may have cached answers
// built on its own. One nobody asked yet (a clone being built) has no users
// to tell.
if (mayAdvanceCached())
{
mayAdvanceChanged();
}
DataBindContainer::dataBindsChanged();
}
#ifdef WITH_RIVE_EDITOR
void DataConverter::editorPrepareForDestruction()
{
unbind();
// Owned binds can themselves own converters with arena-backed children.
// Prepare them while all those children still exist. Preparation removes
// each bind from this container, so iterate a snapshot and delete only
// the non-arena binds whose ownership belongs to us.
for (auto* bind : std::vector<DataBind*>(dataBinds()))
{
const bool owned = bind->editorArena() == nullptr;
bind->editorPrepareForDestruction();
if (owned)
delete bind;
}
m_parentDataBind = nullptr;
}
#endif