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