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