blob: 24965ea8d8aa180b56307764c703b4ec372bb0ae [file]
#include "rive/core.hpp"
#include "rive/component_dirt.hpp"
#include "rive/data_bind/data_bind.hpp"
#include <cassert>
using namespace rive;
Core::~Core()
{
// Detach any DataBind observers so they don't dangle. Walk the intrusive
// list and null out each observer's target+chain pointer.
DataBind* o = m_firstObserver;
while (o != nullptr)
{
DataBind* next = o->nextObserver();
o->onTargetDestroyed();
o = next;
}
m_firstObserver = nullptr;
}
void Core::notifyPropertyChanged(uint16_t propertyKey)
{
// Hot path: no subscribers — single branch.
if (m_firstObserver == nullptr)
{
return;
}
for (DataBind* o = m_firstObserver; o != nullptr; o = o->nextObserver())
{
if (o->propertyKey() == propertyKey)
{
o->addDirt(ComponentDirt::BindingsTarget, false);
}
}
}
void Core::addPropertyObserver(DataBind* observer)
{
// Prepend to the list — O(1). Order doesn't matter; notifyPropertyChanged
// walks all of them.
//
// Guard: if the caller subscribes the same observer twice without
// unsubscribing in between, the list forms a cycle and
// notifyPropertyChanged loops forever. Walk first to confirm absence;
// observer counts per (target, key) are typically ≤1 so this is cheap.
for (DataBind* o = m_firstObserver; o != nullptr; o = o->nextObserver())
{
if (o == observer)
{
assert(false &&
"DataBind already subscribed — double subscribe would "
"form a list cycle");
return;
}
}
observer->setNextObserver(m_firstObserver);
m_firstObserver = observer;
}
void Core::removePropertyObserver(DataBind* observer)
{
DataBind** link = &m_firstObserver;
while (*link != nullptr)
{
if (*link == observer)
{
*link = observer->nextObserver();
observer->setNextObserver(nullptr);
return;
}
link = &(*link)->nextObserverRef();
}
}