| #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_artboard.hpp" |
| #include "rive/data_bind/bindable_property_asset.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_list.hpp" |
| #include "rive/data_bind/bindable_property_boolean.hpp" |
| #include "rive/data_bind/bindable_property_trigger.hpp" |
| #include "rive/data_bind/bindable_property_integer.hpp" |
| #include "rive/data_bind/data_bind_container.hpp" |
| #include "rive/data_bind/context/context_value.hpp" |
| #include "rive/data_bind/context/context_value_asset_image.hpp" |
| #include "rive/data_bind/context/context_value_artboard.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/context/context_value_symbol_list_index.hpp" |
| #include "rive/data_bind/data_values/data_type.hpp" |
| #include "rive/data_bind/converters/data_converter.hpp" |
| #include "rive/data_bind/converters/formula/formula_token.hpp" |
| #include "rive/animation/transition_viewmodel_condition.hpp" |
| #include "rive/animation/state_machine.hpp" |
| #include "rive/artboard_component_list.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; |
| } |
| file(backboardImporter->file()); |
| backboardImporter->addDataConverterReferencer(this); |
| if (target()) |
| { |
| if (target()->is<DataConverter>()) |
| { |
| target()->as<DataConverter>()->addDataBind(this); |
| } |
| else if (target()->is<FormulaToken>()) |
| { |
| target()->as<FormulaToken>()->addDataBind(this); |
| } |
| else |
| { |
| switch (target()->coreType()) |
| { |
| case BindablePropertyNumberBase::typeKey: |
| case BindablePropertyStringBase::typeKey: |
| case BindablePropertyBooleanBase::typeKey: |
| case BindablePropertyEnumBase::typeKey: |
| case BindablePropertyArtboardBase::typeKey: |
| case BindablePropertyColorBase::typeKey: |
| case BindablePropertyTriggerBase::typeKey: |
| case BindablePropertyIntegerBase::typeKey: |
| case BindablePropertyAssetBase::typeKey: |
| case BindablePropertyListBase::typeKey: |
| case TransitionPropertyViewModelComparatorBase::typeKey: |
| case StateTransitionBase::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() && converter()->outputType() != DataType::input && |
| converter() && converter()->outputType() != DataType::none) |
| { |
| return converter()->outputType(); |
| } |
| return sourceOutputType(); |
| } |
| |
| DataType DataBind::sourceOutputType() |
| { |
| if (m_Source != nullptr) |
| { |
| 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; |
| case ViewModelInstanceSymbolListIndexBase::typeKey: |
| return DataType::symbolListIndex; |
| case ViewModelInstanceAssetImageBase::typeKey: |
| return DataType::assetImage; |
| case ViewModelInstanceArtboardBase::typeKey: |
| return DataType::artboard; |
| } |
| } |
| return DataType::none; |
| } |
| |
| void DataBind::source(ViewModelInstanceValue* value) |
| { |
| if (!bindsOnce()) |
| { |
| value->addDependent(this); |
| value->ref(); |
| } |
| m_Source = value; |
| |
| // We treat this as a special case. If an ArtboardComponentList's list |
| // property is bound to a number instance, we know that the component |
| // will be provided with dettached view model instances that need to be |
| // advanced explicitly |
| if (m_Source && target() && target()->is<ArtboardComponentList>()) |
| { |
| target()->as<ArtboardComponentList>()->shouldResetInstances( |
| m_Source->coreType() == ViewModelInstanceNumberBase::typeKey); |
| } |
| } |
| |
| void DataBind::clearSource() |
| { |
| if (m_Source != nullptr) |
| { |
| |
| if (!bindsOnce()) |
| { |
| m_Source->removeDependent(this); |
| m_Source->unref(); |
| } |
| m_Source = nullptr; |
| } |
| } |
| |
| DataBind::~DataBind() |
| { |
| unbind(); |
| 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); |
| break; |
| case DataType::trigger: |
| m_ContextValue = new DataBindContextValueTrigger(this); |
| break; |
| case DataType::symbolListIndex: |
| m_ContextValue = new DataBindContextValueSymbolListIndex(this); |
| break; |
| case DataType::assetImage: |
| m_ContextValue = new DataBindContextValueAssetImage(this); |
| break; |
| case DataType::artboard: |
| m_ContextValue = new DataBindContextValueArtboard(this); |
| break; |
| default: |
| break; |
| } |
| addDirt(ComponentDirt::Bindings, true); |
| } |
| |
| void DataBind::unbind() |
| { |
| clearSource(); |
| if (m_dataConverter != nullptr) |
| { |
| m_dataConverter->unbind(); |
| } |
| if (m_ContextValue != nullptr) |
| { |
| delete m_ContextValue; |
| m_ContextValue = nullptr; |
| } |
| } |
| |
| bool DataBind::canSkip() |
| { |
| return m_target && m_target->is<Component>() && |
| m_target->as<Component>()->isCollapsed() && |
| propertyKey() != LayoutComponentStyleBase::displayValuePropertyKey; |
| } |
| |
| void DataBind::update(ComponentDirt value) |
| { |
| if (m_Source != nullptr && m_ContextValue != nullptr) |
| { |
| 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::updateDependents() |
| { |
| if (m_dataConverter) |
| { |
| m_dataConverter->update(); |
| } |
| } |
| |
| void DataBind::updateSourceBinding(bool invalidate) |
| { |
| if (toSource()) |
| { |
| if (m_ContextValue != nullptr) |
| { |
| if (invalidate) |
| { |
| m_ContextValue->invalidate(); |
| } |
| m_ContextValue->applyToSource(m_target, |
| propertyKey(), |
| isMainToSource()); |
| } |
| } |
| } |
| |
| bool DataBind::isMainToSource() |
| { |
| auto flagsValue = static_cast<DataBindFlags>(flags()); |
| return (flagsValue & DataBindFlags::Direction) == DataBindFlags::ToSource; |
| } |
| |
| bool DataBind::sourceToTargetRunsFirst() |
| { |
| auto flagsValue = static_cast<DataBindFlags>(flags()); |
| return (flagsValue & DataBindFlags::SourceToTargetRunsFirst) == |
| DataBindFlags::SourceToTargetRunsFirst; |
| } |
| |
| void DataBind::addDirt(ComponentDirt value, bool recurse) |
| { |
| if (m_suppressDirt || (m_Dirt & value) == value) |
| { |
| // Already marked. |
| return; |
| } |
| |
| m_Dirt |= value; |
| #ifdef WITH_RIVE_TOOLS |
| if (m_changedCallback != nullptr) |
| { |
| m_changedCallback(); |
| } |
| #endif |
| if ((m_Dirt & ComponentDirt::Dependents) != 0 && m_ContextValue != nullptr) |
| { |
| m_ContextValue->invalidate(); |
| } |
| if (m_container) |
| { |
| m_container->addDirtyDataBind(this); |
| } |
| } |
| |
| void DataBind::container(DataBindContainer* container) |
| { |
| m_container = container; |
| } |
| |
| 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; |
| } |
| |
| bool DataBind::advance(float elapsedTime) |
| { |
| if (converter()) |
| { |
| return converter()->advance(elapsedTime); |
| } |
| return false; |
| } |
| |
| void DataBind::file(File* value) { m_file = value; }; |
| |
| File* DataBind::file() const { return m_file; }; |