| #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); |
| } |