| #include "rive/data_bind/data_bind.hpp" | |
| #include "rive/artboard.hpp" | |
| #include "rive/data_bind_flags.hpp" | |
| #include "rive/generated/core_registry.hpp" | |
| #include "rive/data_bind/bindable_property_number.hpp" | |
| #include "rive/data_bind/bindable_property_string.hpp" | |
| #include "rive/data_bind/bindable_property_color.hpp" | |
| #include "rive/data_bind/bindable_property_enum.hpp" | |
| #include "rive/data_bind/bindable_property_boolean.hpp" | |
| #include "rive/data_bind/bindable_property_trigger.hpp" | |
| #include "rive/data_bind/context/context_value.hpp" | |
| #include "rive/data_bind/context/context_value_boolean.hpp" | |
| #include "rive/data_bind/context/context_value_number.hpp" | |
| #include "rive/data_bind/context/context_value_string.hpp" | |
| #include "rive/data_bind/context/context_value_enum.hpp" | |
| #include "rive/data_bind/context/context_value_list.hpp" | |
| #include "rive/data_bind/context/context_value_color.hpp" | |
| #include "rive/data_bind/context/context_value_trigger.hpp" | |
| #include "rive/data_bind/data_values/data_type.hpp" | |
| #include "rive/data_bind/converters/data_converter.hpp" | |
| #include "rive/animation/transition_viewmodel_condition.hpp" | |
| #include "rive/animation/state_machine.hpp" | |
| #include "rive/importers/artboard_importer.hpp" | |
| #include "rive/importers/state_machine_importer.hpp" | |
| #include "rive/importers/backboard_importer.hpp" | |
| using namespace rive; | |
| StatusCode DataBind::onAddedDirty(CoreContext* context) | |
| { | |
| StatusCode code = Super::onAddedDirty(context); | |
| if (code != StatusCode::Ok) | |
| { | |
| return code; | |
| } | |
| return StatusCode::Ok; | |
| } | |
| StatusCode DataBind::import(ImportStack& importStack) | |
| { | |
| auto backboardImporter = | |
| importStack.latest<BackboardImporter>(Backboard::typeKey); | |
| if (backboardImporter == nullptr) | |
| { | |
| return StatusCode::MissingObject; | |
| } | |
| backboardImporter->addDataConverterReferencer(this); | |
| if (target()) | |
| { | |
| if (target()->is<DataConverter>()) | |
| { | |
| target()->as<DataConverter>()->addDataBind(this); | |
| } | |
| else | |
| { | |
| switch (target()->coreType()) | |
| { | |
| case BindablePropertyNumberBase::typeKey: | |
| case BindablePropertyStringBase::typeKey: | |
| case BindablePropertyBooleanBase::typeKey: | |
| case BindablePropertyEnumBase::typeKey: | |
| case BindablePropertyColorBase::typeKey: | |
| case BindablePropertyTriggerBase::typeKey: | |
| case TransitionPropertyViewModelComparatorBase::typeKey: | |
| { | |
| auto stateMachineImporter = | |
| importStack.latest<StateMachineImporter>( | |
| StateMachineBase::typeKey); | |
| if (stateMachineImporter != nullptr) | |
| { | |
| stateMachineImporter->addDataBind( | |
| std::unique_ptr<DataBind>(this)); | |
| return Super::import(importStack); | |
| } | |
| break; | |
| } | |
| default: | |
| { | |
| auto artboardImporter = | |
| importStack.latest<ArtboardImporter>( | |
| ArtboardBase::typeKey); | |
| if (artboardImporter != nullptr) | |
| { | |
| artboardImporter->addDataBind(this); | |
| return Super::import(importStack); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| return Super::import(importStack); | |
| } | |
| DataType DataBind::outputType() | |
| { | |
| if (converter()) | |
| { | |
| return converter()->outputType(); | |
| } | |
| switch (m_Source->coreType()) | |
| { | |
| case ViewModelInstanceNumberBase::typeKey: | |
| return DataType::number; | |
| case ViewModelInstanceStringBase::typeKey: | |
| return DataType::string; | |
| case ViewModelInstanceEnumBase::typeKey: | |
| return DataType::enumType; | |
| case ViewModelInstanceColorBase::typeKey: | |
| return DataType::color; | |
| case ViewModelInstanceBooleanBase::typeKey: | |
| return DataType::boolean; | |
| case ViewModelInstanceListBase::typeKey: | |
| return DataType::list; | |
| case ViewModelInstanceTriggerBase::typeKey: | |
| return DataType::trigger; | |
| } | |
| return DataType::none; | |
| } | |
| DataBind::~DataBind() | |
| { | |
| delete m_ContextValue; | |
| m_ContextValue = nullptr; | |
| delete m_dataConverter; | |
| m_dataConverter = nullptr; | |
| } | |
| void DataBind::bind() | |
| { | |
| delete m_ContextValue; | |
| m_ContextValue = nullptr; | |
| switch (outputType()) | |
| { | |
| case DataType::number: | |
| m_ContextValue = new DataBindContextValueNumber(this); | |
| break; | |
| case DataType::string: | |
| m_ContextValue = new DataBindContextValueString(this); | |
| break; | |
| case DataType::boolean: | |
| m_ContextValue = new DataBindContextValueBoolean(this); | |
| break; | |
| case DataType::color: | |
| m_ContextValue = new DataBindContextValueColor(this); | |
| break; | |
| case DataType::enumType: | |
| m_ContextValue = new DataBindContextValueEnum(this); | |
| break; | |
| case DataType::list: | |
| m_ContextValue = new DataBindContextValueList(this); | |
| m_ContextValue->update(m_target); | |
| break; | |
| case DataType::trigger: | |
| m_ContextValue = new DataBindContextValueTrigger(this); | |
| break; | |
| default: | |
| break; | |
| } | |
| addDirt(ComponentDirt::Bindings, true); | |
| } | |
| void DataBind::unbind() | |
| { | |
| if (m_ContextValue != nullptr) | |
| { | |
| m_ContextValue->dispose(); | |
| m_ContextValue = nullptr; | |
| } | |
| } | |
| void DataBind::update(ComponentDirt value) | |
| { | |
| if ((value & ComponentDirt::Dependents) == ComponentDirt::Dependents && | |
| m_dataConverter != nullptr) | |
| { | |
| m_dataConverter->update(); | |
| } | |
| if (m_Source != nullptr && m_ContextValue != nullptr) | |
| { | |
| // Use the ComponentDirt::Components flag to indicate the viewmodel has | |
| // added or removed an element to a list. | |
| if ((value & ComponentDirt::Components) == ComponentDirt::Components) | |
| { | |
| m_ContextValue->update(m_target); | |
| } | |
| if ((value & ComponentDirt::Bindings) == ComponentDirt::Bindings) | |
| { | |
| // TODO: @hernan review how dirt and mode work together. If dirt is | |
| // not set for certain modes, we might be able to skip the mode | |
| // validation. | |
| auto flagsValue = static_cast<DataBindFlags>(flags()); | |
| if (toTarget()) | |
| { | |
| m_ContextValue->apply(m_target, | |
| propertyKey(), | |
| (flagsValue & DataBindFlags::Direction) == | |
| DataBindFlags::ToTarget); | |
| } | |
| } | |
| } | |
| } | |
| void DataBind::updateSourceBinding() | |
| { | |
| auto flagsValue = static_cast<DataBindFlags>(flags()); | |
| if (toSource()) | |
| { | |
| if (m_ContextValue != nullptr) | |
| { | |
| m_ContextValue->applyToSource( | |
| m_target, | |
| propertyKey(), | |
| (flagsValue & DataBindFlags::Direction) == | |
| DataBindFlags::ToSource); | |
| } | |
| } | |
| } | |
| bool DataBind::addDirt(ComponentDirt value, bool recurse) | |
| { | |
| if ((m_Dirt & value) == value) | |
| { | |
| // Already marked. | |
| return false; | |
| } | |
| m_Dirt |= value; | |
| #ifdef WITH_RIVE_TOOLS | |
| if (m_changedCallback != nullptr) | |
| { | |
| m_changedCallback(); | |
| } | |
| #endif | |
| if (target() != nullptr && target()->is<DataConverter>()) | |
| { | |
| target()->as<DataConverter>()->addDirt(value); | |
| } | |
| return true; | |
| } | |
| bool DataBind::bindsOnce() | |
| { | |
| auto flagsValue = static_cast<DataBindFlags>(flags()); | |
| return (flagsValue & DataBindFlags::Once) == DataBindFlags::Once; | |
| } | |
| bool DataBind::toSource() | |
| { | |
| auto flagsValue = static_cast<DataBindFlags>(flags()); | |
| return (flagsValue & (DataBindFlags::TwoWay | DataBindFlags::ToSource)) != | |
| 0; | |
| } | |
| bool DataBind::toTarget() | |
| { | |
| auto flagsValue = static_cast<DataBindFlags>(flags()); | |
| return (flagsValue & DataBindFlags::TwoWay) != 0 || | |
| (flagsValue & DataBindFlags::ToSource) == 0; | |
| } |