mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
Artboard object validation
This introduces an iterative validation cycle which allows for multi-level (parent->parent) validation by re-running the check whenever an object's validation state (valid/invalid) changes in the cycle. Based on discussion here: https://2dimensions.slack.com/archives/CLLCU09T6/p1730395496000459 This makes it so an object which doesn't find a valid parent can itself be removed from the artboard and other objects which then depend on it can also invalidate if necessary. Diffs= ce39f8be9f Artboard object validation (#8464) Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
bdb9eb01db0e9aac7a22708e72576c301cc071dc
|
||||
ce39f8be9f60fd42581b088d1b523c1bb1a01139
|
||||
|
||||
@@ -108,6 +108,7 @@ public:
|
||||
public:
|
||||
Artboard();
|
||||
~Artboard() override;
|
||||
bool validateObjects();
|
||||
StatusCode initialize();
|
||||
|
||||
Core* resolve(uint32_t id) const override;
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
DependencyHelper<Artboard, Component> m_DependencyHelper;
|
||||
virtual bool collapse(bool value);
|
||||
inline Artboard* artboard() const { return m_Artboard; }
|
||||
bool validate(CoreContext* context) override;
|
||||
StatusCode onAddedDirty(CoreContext* context) override;
|
||||
inline ContainerComponent* parent() const { return m_Parent; }
|
||||
const std::vector<Component*>& dependents() const
|
||||
|
||||
@@ -12,6 +12,8 @@ protected:
|
||||
|
||||
public:
|
||||
void buildDependencies() override;
|
||||
|
||||
bool validate(CoreContext* context) override;
|
||||
StatusCode onAddedDirty(CoreContext* context) override;
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
@@ -46,6 +46,9 @@ public:
|
||||
return static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
/// Called to validate the object can be used at runtime.
|
||||
virtual bool validate(CoreContext* context) { return true; }
|
||||
|
||||
/// Called when the object is first added to the context, other objects
|
||||
/// may not have resolved their dependencies yet. This is an opportunity
|
||||
/// to look up objects referenced by id, but not assume that they in
|
||||
|
||||
@@ -61,6 +61,7 @@ private:
|
||||
class NestedAnimation : public NestedAnimationBase
|
||||
{
|
||||
public:
|
||||
bool validate(CoreContext* context) override;
|
||||
StatusCode onAddedDirty(CoreContext* context) override;
|
||||
|
||||
// Advance animations and apply them to the artboard.
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
#include "rive/nested_animation.hpp"
|
||||
#include "rive/container_component.hpp"
|
||||
#include "rive/nested_artboard.hpp"
|
||||
#include "rive/core_context.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
bool NestedAnimation::validate(CoreContext* context)
|
||||
{
|
||||
if (!Super::validate(context))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto parentObject = context->resolve(parentId());
|
||||
// We know parentObject is not null from Super::validate().
|
||||
return parentObject->is<NestedArtboard>();
|
||||
}
|
||||
|
||||
StatusCode NestedAnimation::onAddedDirty(CoreContext* context)
|
||||
{
|
||||
StatusCode code = Super::onAddedDirty(context);
|
||||
if (code == StatusCode::Ok)
|
||||
{
|
||||
if (!parent()->is<NestedArtboard>())
|
||||
{
|
||||
return StatusCode::InvalidObject;
|
||||
}
|
||||
auto nestedArtboard = parent()->as<NestedArtboard>();
|
||||
nestedArtboard->addNestedAnimation(this);
|
||||
}
|
||||
|
||||
@@ -96,6 +96,54 @@ static bool canContinue(StatusCode code)
|
||||
return code != StatusCode::InvalidObject;
|
||||
}
|
||||
|
||||
bool Artboard::validateObjects()
|
||||
{
|
||||
auto size = m_Objects.size();
|
||||
std::vector<bool> valid(size);
|
||||
|
||||
// Max iterations..
|
||||
for (int cycle = 0; cycle < 100; cycle++)
|
||||
{
|
||||
bool changed = false;
|
||||
for (size_t i = 1; i < size; i++)
|
||||
{
|
||||
auto object = m_Objects[i];
|
||||
if (object == nullptr)
|
||||
{
|
||||
// objects can be null if they were not understood by this
|
||||
// runtime.
|
||||
continue;
|
||||
}
|
||||
bool wasValid = valid[i];
|
||||
bool isValid = object->validate(this);
|
||||
if (wasValid != isValid)
|
||||
{
|
||||
changed = true;
|
||||
valid[i] = isValid;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
// Delete invalid objects.
|
||||
for (size_t i = 1; i < size; i++)
|
||||
{
|
||||
if (valid[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
delete m_Objects[i];
|
||||
m_Objects[i] = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StatusCode Artboard::initialize()
|
||||
{
|
||||
StatusCode code;
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
|
||||
using namespace rive;
|
||||
|
||||
bool Component::validate(CoreContext* context)
|
||||
{
|
||||
auto coreObject = context->resolve(parentId());
|
||||
return coreObject != nullptr && coreObject->is<ContainerComponent>();
|
||||
}
|
||||
|
||||
StatusCode Component::onAddedDirty(CoreContext* context)
|
||||
{
|
||||
m_Artboard = static_cast<Artboard*>(context);
|
||||
@@ -18,12 +24,7 @@ StatusCode Component::onAddedDirty(CoreContext* context)
|
||||
// We're the artboard, don't parent to ourselves.
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
auto coreObject = context->resolve(parentId());
|
||||
if (coreObject == nullptr || !coreObject->is<ContainerComponent>())
|
||||
{
|
||||
return StatusCode::MissingObject;
|
||||
}
|
||||
m_Parent = static_cast<ContainerComponent*>(coreObject);
|
||||
m_Parent = context->resolve(parentId())->as<ContainerComponent>();
|
||||
m_Parent->addChild(this);
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,16 @@
|
||||
|
||||
using namespace rive;
|
||||
|
||||
bool TargetedConstraint::validate(CoreContext* context)
|
||||
{
|
||||
if (!Super::validate(context))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto coreObject = context->resolve(targetId());
|
||||
return coreObject != nullptr && coreObject->is<TransformComponent>();
|
||||
}
|
||||
|
||||
StatusCode TargetedConstraint::onAddedDirty(CoreContext* context)
|
||||
{
|
||||
StatusCode code = Super::onAddedDirty(context);
|
||||
@@ -11,13 +21,7 @@ StatusCode TargetedConstraint::onAddedDirty(CoreContext* context)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
auto coreObject = context->resolve(targetId());
|
||||
if (coreObject == nullptr || !coreObject->is<TransformComponent>())
|
||||
{
|
||||
return StatusCode::MissingObject;
|
||||
}
|
||||
|
||||
m_Target = static_cast<TransformComponent*>(coreObject);
|
||||
m_Target = context->resolve(targetId())->as<TransformComponent>();
|
||||
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
@@ -26,8 +30,5 @@ void TargetedConstraint::buildDependencies()
|
||||
{
|
||||
// Targeted constraints must have their constrained component (parent)
|
||||
// update after the target.
|
||||
if (m_Target != nullptr)
|
||||
{
|
||||
m_Target->addDependent(parent());
|
||||
}
|
||||
m_Target->addDependent(parent());
|
||||
}
|
||||
|
||||
@@ -31,7 +31,14 @@ void ArtboardImporter::addDataBind(DataBind* dataBind)
|
||||
m_Artboard->addDataBind(dataBind);
|
||||
}
|
||||
|
||||
StatusCode ArtboardImporter::resolve() { return m_Artboard->initialize(); }
|
||||
StatusCode ArtboardImporter::resolve()
|
||||
{
|
||||
if (!m_Artboard->validateObjects())
|
||||
{
|
||||
return StatusCode::InvalidObject;
|
||||
}
|
||||
return m_Artboard->initialize();
|
||||
}
|
||||
|
||||
bool ArtboardImporter::readNullObject()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user