blob: 5d09ca09c8310aafd97c7782706567d90d96393f [file]
#include "rive/animation/keyed_object.hpp"
#include "rive/animation/keyed_property.hpp"
#include "rive/animation/linear_animation.hpp"
#include "rive/artboard.hpp"
#include "rive/layout_component.hpp"
#include "rive/importers/linear_animation_importer.hpp"
#include "rive/generated/core_registry.hpp"
using namespace rive;
KeyedObject::KeyedObject() {}
KeyedObject::~KeyedObject() {}
void KeyedObject::addKeyedProperty(std::unique_ptr<KeyedProperty> property)
{
m_keyedProperties.push_back(std::move(property));
}
StatusCode KeyedObject::onAddedDirty(CoreContext* context)
{
// Make sure we're keying a valid object.
Core* coreObject = context->resolve(objectId());
if (coreObject == nullptr)
{
return StatusCode::MissingObject;
}
// `clip` is animatable; a layout whose clip is keyed needs its
// DrawableProxy up front so it exists before the artboard's one-time proxy
// injection, even while clip is currently false. This runs on the source
// only (instances share animations); LayoutComponent::clone carries the
// flag to instances.
const bool isLayout = coreObject->is<LayoutComponent>();
for (auto itr = m_keyedProperties.begin(); itr != m_keyedProperties.end();)
{
auto& property = *itr;
// Validate coreObject supports propertyKey, if not remove it from the
// property keys.
if (!CoreRegistry::objectSupportsProperty(coreObject,
property->propertyKey()))
{
itr = m_keyedProperties.erase(itr);
continue;
}
if (isLayout &&
property->propertyKey() == LayoutComponentBase::clipPropertyKey)
{
coreObject->as<LayoutComponent>()->markClipMayBeDynamic();
}
StatusCode code;
if ((code = property->onAddedDirty(context)) != StatusCode::Ok)
{
return code;
}
itr++;
}
#ifdef WITH_RIVE_EDITOR
// The editor list is rebuilt by `finalizeBatch` AFTER Pass 3
// onAddedDirty runs, so at the time the dispatcher calls us on a
// freshly-hydrated KeyedObject it's still empty. On subsequent
// finalizeBatch runs (new coop batch), the editor list carries
// the previously-wired coop keyed properties; walking it again
// here keeps CoreRegistry::objectSupportsProperty pruning
// consistent with the runtime path.
for (auto itr = m_editorKeyedProperties.begin();
itr != m_editorKeyedProperties.end();)
{
auto* property = *itr;
if (!CoreRegistry::objectSupportsProperty(coreObject,
property->propertyKey()))
{
itr = m_editorKeyedProperties.erase(itr);
continue;
}
property->onAddedDirty(context);
itr++;
}
#endif
return StatusCode::Ok;
}
StatusCode KeyedObject::onAddedClean(CoreContext* context)
{
for (auto& property : m_keyedProperties)
{
property->onAddedClean(context);
}
#ifdef WITH_RIVE_EDITOR
for (auto* property : m_editorKeyedProperties)
{
property->onAddedClean(context);
}
#endif
return StatusCode::Ok;
}
// `addKeyedPropertyForEditor` and `clearEditorKeyedProperties` live in
// `editor_native/native/src/editor/animation/keyed_object_editor.cpp`
// — see the matching comment in `keyed_property.cpp`.
void KeyedObject::reportKeyedCallbacks(KeyedCallbackReporter* reporter,
float secondsFrom,
float secondsTo,
bool isAtStartFrame) const
{
for (const std::unique_ptr<KeyedProperty>& property : m_keyedProperties)
{
if (!CoreRegistry::isCallback(property->propertyKey()))
{
continue;
}
property->reportKeyedCallbacks(reporter,
objectId(),
secondsFrom,
secondsTo,
isAtStartFrame);
}
#ifdef WITH_RIVE_EDITOR
for (auto* property : m_editorKeyedProperties)
{
if (!CoreRegistry::isCallback(property->propertyKey()))
{
continue;
}
property->reportKeyedCallbacks(reporter,
objectId(),
secondsFrom,
secondsTo,
isAtStartFrame);
}
#endif
}
void KeyedObject::apply(Artboard* artboard,
float time,
float mix,
const LinearAnimationInstance* context)
{
Core* object = artboard->resolve(objectId());
if (object == nullptr)
{
return;
}
for (std::unique_ptr<KeyedProperty>& property : m_keyedProperties)
{
if (CoreRegistry::isCallback(property->propertyKey()))
{
continue;
}
property->apply(object, time, mix, context);
}
#ifdef WITH_RIVE_EDITOR
for (auto* property : m_editorKeyedProperties)
{
if (CoreRegistry::isCallback(property->propertyKey()))
{
continue;
}
property->apply(object, time, mix);
}
#endif
}
StatusCode KeyedObject::import(ImportStack& importStack)
{
auto importer = importStack.latest<LinearAnimationImporter>(
LinearAnimationBase::typeKey);
if (importer == nullptr)
{
return StatusCode::MissingObject;
}
// we transfer ownership of ourself to the importer!
importer->addKeyedObject(std::unique_ptr<KeyedObject>(this));
return Super::import(importStack);
}