add data converter and data types for conversion First PR for converters support. It adds the abstract data_converter class And adds new data value objects that can be passed between converters to easily transform one type of data to another https://github.com/user-attachments/assets/e8a0fdee-8845-4d0d-a894-c948c2b0a209 Diffs= 4bf7c7545 add data converter and data types for conversion (#7734) Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head index a891ec7..b080470 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -d9f5701ec10c0d0eecb0e3d2f5b0ee80499c746d +4bf7c75453f0fc3fec1519a9a3dfe7a4ed63a054
diff --git a/dev/defs/data_bind/converters/data_converter.json b/dev/defs/data_bind/converters/data_converter.json new file mode 100644 index 0000000..b5a0c49 --- /dev/null +++ b/dev/defs/data_bind/converters/data_converter.json
@@ -0,0 +1,30 @@ +{ + "name": "DataConverter", + "key": { + "int": 488, + "string": "dataconverter" + }, + "abstract": true, + "properties": { + "order": { + "type": "FractionalIndex", + "initialValue": "FractionalIndex.invalid", + "initialValueRuntime": "0", + "key": { + "int": 661, + "string": "order" + }, + "description": "Order value for sorting data converters", + "runtime": false + }, + "name": { + "type": "String", + "initialValue": "''", + "key": { + "int": 662, + "string": "name" + }, + "description": "Non-unique identifier, used to give friendly names to data converters." + } + } +} \ No newline at end of file
diff --git a/dev/defs/data_bind/data_bind.json b/dev/defs/data_bind/data_bind.json index 3e3124d..3ec525b 100644 --- a/dev/defs/data_bind/data_bind.json +++ b/dev/defs/data_bind/data_bind.json
@@ -31,6 +31,17 @@ "int": 587, "string": "flags" } + }, + "converterId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 660, + "string": "converterid" + }, + "description": "Identifier used to link to a data converter." } } } \ No newline at end of file
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index 2e78c7e..e3cf802 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp
@@ -295,6 +295,7 @@ { auto dataBindClone = static_cast<DataBind*>(dataBind->clone()); dataBindClone->target(cloneObjects.back()); + dataBindClone->converter(dataBind->converter()); artboardClone->m_DataBinds.push_back(dataBindClone); } }
diff --git a/include/rive/data_bind/context/context_value.hpp b/include/rive/data_bind/context/context_value.hpp index 370f46f..c26730b 100644 --- a/include/rive/data_bind/context/context_value.hpp +++ b/include/rive/data_bind/context/context_value.hpp
@@ -1,21 +1,48 @@ #ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_HPP_ #define _RIVE_DATA_BIND_CONTEXT_VALUE_HPP_ -#include "rive/refcnt.hpp" #include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/data_bind/converters/data_converter.hpp" #include <stdio.h> namespace rive { class DataBindContextValue { protected: - ViewModelInstanceValue* m_Source; + ViewModelInstanceValue* m_source; + DataConverter* m_converter; + DataValue* m_dataValue; public: - DataBindContextValue(){}; + DataBindContextValue(ViewModelInstanceValue* source, DataConverter* converter); virtual ~DataBindContextValue(){}; - virtual void applyToSource(Core* component, uint32_t propertyKey){}; - virtual void apply(Core* component, uint32_t propertyKey){}; + virtual void applyToSource(Core* component, uint32_t propertyKey, bool isMainDirection); + virtual void apply(Core* component, uint32_t propertyKey, bool isMainDirection){}; virtual void update(Core* component){}; + virtual DataValue* getTargetValue(Core* target, uint32_t propertyKey) { return nullptr; }; + void updateSourceValue(); + template <typename T = DataValue, typename U> U getDataValue(DataValue* input) + { + auto dataValue = m_converter != nullptr ? m_converter->convert(input) : input; + if (dataValue->is<T>()) + { + return dataValue->as<T>()->value(); + } + return (new T())->value(); + }; + template <typename T = DataValue, typename U> U getReverseDataValue(DataValue* input) + { + auto dataValue = m_converter != nullptr ? m_converter->reverseConvert(input) : input; + if (dataValue->is<T>()) + { + return dataValue->as<T>()->value(); + } + return (new T())->value(); + }; + template <typename T = DataValue, typename U> + U calculateValue(DataValue* input, bool isMainDirection) + { + return isMainDirection ? getDataValue<T, U>(input) : getReverseDataValue<T, U>(input); + }; }; } // namespace rive
diff --git a/include/rive/data_bind/context/context_value_boolean.hpp b/include/rive/data_bind/context/context_value_boolean.hpp index 82e9a7f..678e7fc 100644 --- a/include/rive/data_bind/context/context_value_boolean.hpp +++ b/include/rive/data_bind/context/context_value_boolean.hpp
@@ -7,12 +7,9 @@ { public: - DataBindContextValueBoolean(ViewModelInstanceValue* value); - void apply(Core* component, uint32_t propertyKey) override; - virtual void applyToSource(Core* component, uint32_t propertyKey) override; - -private: - bool m_Value; + DataBindContextValueBoolean(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + DataValue* getTargetValue(Core* target, uint32_t propertyKey) override; }; } // namespace rive
diff --git a/include/rive/data_bind/context/context_value_color.hpp b/include/rive/data_bind/context/context_value_color.hpp index 22fe8f8..207062a 100644 --- a/include/rive/data_bind/context/context_value_color.hpp +++ b/include/rive/data_bind/context/context_value_color.hpp
@@ -7,12 +7,9 @@ { public: - DataBindContextValueColor(ViewModelInstanceValue* value); - void apply(Core* component, uint32_t propertyKey) override; - virtual void applyToSource(Core* component, uint32_t propertyKey) override; - -private: - double m_Value; + DataBindContextValueColor(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + DataValue* getTargetValue(Core* target, uint32_t propertyKey) override; }; } // namespace rive
diff --git a/include/rive/data_bind/context/context_value_enum.hpp b/include/rive/data_bind/context/context_value_enum.hpp index 98c21c4..dd097b2 100644 --- a/include/rive/data_bind/context/context_value_enum.hpp +++ b/include/rive/data_bind/context/context_value_enum.hpp
@@ -7,12 +7,9 @@ { public: - DataBindContextValueEnum(ViewModelInstanceValue* value); - void apply(Core* component, uint32_t propertyKey) override; - virtual void applyToSource(Core* component, uint32_t propertyKey) override; - -private: - int m_Value; + DataBindContextValueEnum(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + DataValue* getTargetValue(Core* target, uint32_t propertyKey) override; }; } // namespace rive
diff --git a/include/rive/data_bind/context/context_value_list.hpp b/include/rive/data_bind/context/context_value_list.hpp index 0c12f91..5dd9586 100644 --- a/include/rive/data_bind/context/context_value_list.hpp +++ b/include/rive/data_bind/context/context_value_list.hpp
@@ -11,10 +11,12 @@ { public: - DataBindContextValueList(ViewModelInstanceValue* value); - void apply(Core* component, uint32_t propertyKey) override; + DataBindContextValueList(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; void update(Core* target) override; - virtual void applyToSource(Core* component, uint32_t propertyKey) override; + virtual void applyToSource(Core* component, + uint32_t propertyKey, + bool isMainDirection) override; private: std::vector<std::unique_ptr<DataBindContextValueListItem>> m_ListItemsCache;
diff --git a/include/rive/data_bind/context/context_value_number.hpp b/include/rive/data_bind/context/context_value_number.hpp index b2d850a..f3a56fa 100644 --- a/include/rive/data_bind/context/context_value_number.hpp +++ b/include/rive/data_bind/context/context_value_number.hpp
@@ -7,12 +7,9 @@ { public: - DataBindContextValueNumber(ViewModelInstanceValue* value); - void apply(Core* component, uint32_t propertyKey) override; - virtual void applyToSource(Core* component, uint32_t propertyKey) override; - -private: - double m_Value; + DataBindContextValueNumber(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + DataValue* getTargetValue(Core* target, uint32_t propertyKey) override; }; } // namespace rive
diff --git a/include/rive/data_bind/context/context_value_string.hpp b/include/rive/data_bind/context/context_value_string.hpp index 0b32528..fb8f3cf 100644 --- a/include/rive/data_bind/context/context_value_string.hpp +++ b/include/rive/data_bind/context/context_value_string.hpp
@@ -7,12 +7,9 @@ { public: - DataBindContextValueString(ViewModelInstanceValue* value); - void apply(Core* component, uint32_t propertyKey) override; - virtual void applyToSource(Core* component, uint32_t propertyKey) override; - -private: - std::string m_Value; + DataBindContextValueString(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + DataValue* getTargetValue(Core* target, uint32_t propertyKey) override; }; } // namespace rive
diff --git a/include/rive/data_bind/converters/data_converter.hpp b/include/rive/data_bind/converters/data_converter.hpp new file mode 100644 index 0000000..bf6deb6 --- /dev/null +++ b/include/rive/data_bind/converters/data_converter.hpp
@@ -0,0 +1,18 @@ +#ifndef _RIVE_DATA_CONVERTER_HPP_ +#define _RIVE_DATA_CONVERTER_HPP_ +#include "rive/generated/data_bind/converters/data_converter_base.hpp" +#include "rive/data_bind/data_values/data_value.hpp" +#include <stdio.h> +namespace rive +{ +class DataConverter : public DataConverterBase +{ +public: + virtual DataValue* convert(DataValue* value) { return value; }; + virtual DataValue* reverseConvert(DataValue* value) { return value; }; + virtual DataType outputType() { return DataType::none; }; + StatusCode import(ImportStack& importStack) override; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/data_bind/data_bind.hpp b/include/rive/data_bind/data_bind.hpp index 0ccccb3..a62cfd6 100644 --- a/include/rive/data_bind/data_bind.hpp +++ b/include/rive/data_bind/data_bind.hpp
@@ -5,6 +5,8 @@ #include "rive/viewmodel/viewmodel_instance_value.hpp" #include "rive/data_bind/context/context_value.hpp" #include "rive/data_bind/data_context.hpp" +#include "rive/data_bind/converters/data_converter.hpp" +#include "rive/data_bind/data_values/data_type.hpp" #include <stdio.h> namespace rive { @@ -21,12 +23,16 @@ ComponentDirt dirt() { return m_Dirt; }; void dirt(ComponentDirt value) { m_Dirt = value; }; bool addDirt(ComponentDirt value, bool recurse); + DataConverter* converter() const { return m_dataConverter; }; + void converter(DataConverter* value) { m_dataConverter = value; }; protected: ComponentDirt m_Dirt = ComponentDirt::Filthy; Core* m_target; ViewModelInstanceValue* m_Source; std::unique_ptr<DataBindContextValue> m_ContextValue; + DataConverter* m_dataConverter; + DataType outputType(); }; } // namespace rive
diff --git a/include/rive/data_bind/data_values/data_type.hpp b/include/rive/data_bind/data_values/data_type.hpp new file mode 100644 index 0000000..c1740b5 --- /dev/null +++ b/include/rive/data_bind/data_values/data_type.hpp
@@ -0,0 +1,30 @@ +#ifndef _RIVE_DATA_TYPE_HPP_ +#define _RIVE_DATA_TYPE_HPP_ +namespace rive +{ +/// Data types used for converters. +enum class DataType : unsigned int +{ + /// None. + none = 0, + + /// String. + string = 1, + + /// Number. + number = 2, + + /// Bool. + boolean = 3, + + /// Color. + color = 4, + + /// List. + list = 5, + + /// Enum. + enumType = 6 +}; +} // namespace rive +#endif \ No newline at end of file
diff --git a/include/rive/data_bind/data_values/data_value.hpp b/include/rive/data_bind/data_values/data_value.hpp new file mode 100644 index 0000000..e412183 --- /dev/null +++ b/include/rive/data_bind/data_values/data_value.hpp
@@ -0,0 +1,21 @@ +#ifndef _RIVE_DATA_VALUE_HPP_ +#define _RIVE_DATA_VALUE_HPP_ +#include "rive/data_bind/data_values/data_type.hpp" + +#include <stdio.h> +namespace rive +{ +class DataValue +{ +public: + virtual bool isTypeOf(DataType dataType) const { return false; } + template <typename T> inline bool is() const { return isTypeOf(T::typeKey); } + template <typename T> inline T* as() + { + assert(is<T>()); + return static_cast<T*>(this); + } +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/data_bind/data_values/data_value_boolean.hpp b/include/rive/data_bind/data_values/data_value_boolean.hpp new file mode 100644 index 0000000..4fd8575 --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_boolean.hpp
@@ -0,0 +1,23 @@ +#ifndef _RIVE_DATA_VALUE_BOOLEAN_HPP_ +#define _RIVE_DATA_VALUE_BOOLEAN_HPP_ +#include "rive/data_bind/data_values/data_value.hpp" + +#include <stdio.h> +namespace rive +{ +class DataValueBoolean : public DataValue +{ +private: + bool m_value = false; + +public: + DataValueBoolean(bool value) : m_value(value){}; + DataValueBoolean(){}; + static const DataType typeKey = DataType::boolean; + bool isTypeOf(DataType typeKey) const override { return typeKey == DataType::boolean; } + bool value() { return m_value; }; + void value(bool value) { m_value = value; }; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/data_bind/data_values/data_value_color.hpp b/include/rive/data_bind/data_values/data_value_color.hpp new file mode 100644 index 0000000..fdc88b5 --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_color.hpp
@@ -0,0 +1,23 @@ +#ifndef _RIVE_DATA_VALUE_COLOR_HPP_ +#define _RIVE_DATA_VALUE_COLOR_HPP_ +#include "rive/data_bind/data_values/data_value.hpp" + +#include <stdio.h> +namespace rive +{ +class DataValueColor : public DataValue +{ +private: + int m_value = false; + +public: + DataValueColor(int value) : m_value(value){}; + DataValueColor(){}; + static const DataType typeKey = DataType::color; + bool isTypeOf(DataType typeKey) const override { return typeKey == DataType::color; } + int value() { return m_value; }; + void value(int value) { m_value = value; }; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/data_bind/data_values/data_value_enum.hpp b/include/rive/data_bind/data_values/data_value_enum.hpp new file mode 100644 index 0000000..9c2dbe5 --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_enum.hpp
@@ -0,0 +1,26 @@ +#ifndef _RIVE_DATA_VALUE_ENUM_HPP_ +#define _RIVE_DATA_VALUE_ENUM_HPP_ +#include "rive/data_bind/data_values/data_value.hpp" +#include "rive/viewmodel/data_enum.hpp" + +#include <iostream> +namespace rive +{ +class DataValueEnum : public DataValue +{ +private: + uint32_t m_value = 0; + DataEnum* m_dataEnum; + +public: + DataValueEnum(uint32_t value, DataEnum* dataEnum) : m_value(value), m_dataEnum(dataEnum){}; + DataValueEnum(){}; + static const DataType typeKey = DataType::enumType; + bool isTypeOf(DataType typeKey) const override { return typeKey == DataType::enumType; }; + uint32_t value() { return m_value; }; + void value(uint32_t value) { m_value = value; }; + DataEnum* dataEnum() { return m_dataEnum; }; + void dataEnum(DataEnum* value) { m_dataEnum = value; }; +}; +} // namespace rive +#endif \ No newline at end of file
diff --git a/include/rive/data_bind/data_values/data_value_number.hpp b/include/rive/data_bind/data_values/data_value_number.hpp new file mode 100644 index 0000000..90a3b5d --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_number.hpp
@@ -0,0 +1,23 @@ +#ifndef _RIVE_DATA_VALUE_NUMBER_HPP_ +#define _RIVE_DATA_VALUE_NUMBER_HPP_ +#include "rive/data_bind/data_values/data_value.hpp" + +#include <stdio.h> +namespace rive +{ +class DataValueNumber : public DataValue +{ +private: + float m_value = 0; + +public: + DataValueNumber(float value) : m_value(value){}; + DataValueNumber(){}; + static const DataType typeKey = DataType::number; + bool isTypeOf(DataType typeKey) const override { return typeKey == DataType::number; } + float value() { return m_value; }; + void value(float value) { m_value = value; }; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/data_bind/data_values/data_value_string.hpp b/include/rive/data_bind/data_values/data_value_string.hpp new file mode 100644 index 0000000..7bf2676 --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_string.hpp
@@ -0,0 +1,22 @@ +#ifndef _RIVE_DATA_VALUE_STRING_HPP_ +#define _RIVE_DATA_VALUE_STRING_HPP_ +#include "rive/data_bind/data_values/data_value.hpp" + +#include <iostream> +namespace rive +{ +class DataValueString : public DataValue +{ +private: + std::string m_value = ""; + +public: + DataValueString(std::string value) : m_value(value){}; + DataValueString(){}; + static const DataType typeKey = DataType::string; + bool isTypeOf(DataType typeKey) const override { return typeKey == DataType::string; }; + std::string value() { return m_value; }; + void value(std::string value) { m_value = value; }; +}; +} // namespace rive +#endif \ No newline at end of file
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index 592e41b..d8e9eb9 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp
@@ -118,6 +118,7 @@ #include "rive/data_bind/bindable_property_enum.hpp" #include "rive/data_bind/bindable_property_number.hpp" #include "rive/data_bind/bindable_property_string.hpp" +#include "rive/data_bind/converters/data_converter.hpp" #include "rive/data_bind/data_bind.hpp" #include "rive/data_bind/data_bind_context.hpp" #include "rive/draw_rules.hpp" @@ -994,6 +995,9 @@ case DataBindBase::flagsPropertyKey: object->as<DataBindBase>()->flags(value); break; + case DataBindBase::converterIdPropertyKey: + object->as<DataBindBase>()->converterId(value); + break; case BindablePropertyEnumBase::propertyValuePropertyKey: object->as<BindablePropertyEnumBase>()->propertyValue(value); break; @@ -1132,6 +1136,9 @@ case OpenUrlEventBase::urlPropertyKey: object->as<OpenUrlEventBase>()->url(value); break; + case DataConverterBase::namePropertyKey: + object->as<DataConverterBase>()->name(value); + break; case BindablePropertyStringBase::propertyValuePropertyKey: object->as<BindablePropertyStringBase>()->propertyValue(value); break; @@ -1999,6 +2006,8 @@ return object->as<DataBindBase>()->propertyKey(); case DataBindBase::flagsPropertyKey: return object->as<DataBindBase>()->flags(); + case DataBindBase::converterIdPropertyKey: + return object->as<DataBindBase>()->converterId(); case BindablePropertyEnumBase::propertyValuePropertyKey: return object->as<BindablePropertyEnumBase>()->propertyValue(); case NestedArtboardLeafBase::fitPropertyKey: @@ -2097,6 +2106,8 @@ return object->as<TransitionValueStringComparatorBase>()->value(); case OpenUrlEventBase::urlPropertyKey: return object->as<OpenUrlEventBase>()->url(); + case DataConverterBase::namePropertyKey: + return object->as<DataConverterBase>()->name(); case BindablePropertyStringBase::propertyValuePropertyKey: return object->as<BindablePropertyStringBase>()->propertyValue(); case TextValueRunBase::textPropertyKey: @@ -2617,6 +2628,7 @@ case OpenUrlEventBase::targetValuePropertyKey: case DataBindBase::propertyKeyPropertyKey: case DataBindBase::flagsPropertyKey: + case DataBindBase::converterIdPropertyKey: case BindablePropertyEnumBase::propertyValuePropertyKey: case NestedArtboardLeafBase::fitPropertyKey: case WeightBase::valuesPropertyKey: @@ -2661,6 +2673,7 @@ case KeyFrameStringBase::valuePropertyKey: case TransitionValueStringComparatorBase::valuePropertyKey: case OpenUrlEventBase::urlPropertyKey: + case DataConverterBase::namePropertyKey: case BindablePropertyStringBase::propertyValuePropertyKey: case TextValueRunBase::textPropertyKey: case CustomPropertyStringBase::propertyValuePropertyKey: @@ -3180,6 +3193,8 @@ return object->is<DataBindBase>(); case DataBindBase::flagsPropertyKey: return object->is<DataBindBase>(); + case DataBindBase::converterIdPropertyKey: + return object->is<DataBindBase>(); case BindablePropertyEnumBase::propertyValuePropertyKey: return object->is<BindablePropertyEnumBase>(); case NestedArtboardLeafBase::fitPropertyKey: @@ -3264,6 +3279,8 @@ return object->is<TransitionValueStringComparatorBase>(); case OpenUrlEventBase::urlPropertyKey: return object->is<OpenUrlEventBase>(); + case DataConverterBase::namePropertyKey: + return object->is<DataConverterBase>(); case BindablePropertyStringBase::propertyValuePropertyKey: return object->is<BindablePropertyStringBase>(); case TextValueRunBase::textPropertyKey:
diff --git a/include/rive/generated/data_bind/converters/data_converter_base.hpp b/include/rive/generated/data_bind/converters/data_converter_base.hpp new file mode 100644 index 0000000..737b567 --- /dev/null +++ b/include/rive/generated/data_bind/converters/data_converter_base.hpp
@@ -0,0 +1,66 @@ +#ifndef _RIVE_DATA_CONVERTER_BASE_HPP_ +#define _RIVE_DATA_CONVERTER_BASE_HPP_ +#include <string> +#include "rive/core.hpp" +#include "rive/core/field_types/core_string_type.hpp" +namespace rive +{ +class DataConverterBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 488; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataConverterBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t namePropertyKey = 662; + +private: + std::string m_Name = ""; + +public: + inline const std::string& name() const { return m_Name; } + void name(std::string value) + { + if (m_Name == value) + { + return; + } + m_Name = value; + nameChanged(); + } + + void copy(const DataConverterBase& object) { m_Name = object.m_Name; } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case namePropertyKey: + m_Name = CoreStringType::deserialize(reader); + return true; + } + return false; + } + +protected: + virtual void nameChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/data_bind/data_bind_base.hpp b/include/rive/generated/data_bind/data_bind_base.hpp index b900663..77db021 100644 --- a/include/rive/generated/data_bind/data_bind_base.hpp +++ b/include/rive/generated/data_bind/data_bind_base.hpp
@@ -29,10 +29,12 @@ static const uint16_t propertyKeyPropertyKey = 586; static const uint16_t flagsPropertyKey = 587; + static const uint16_t converterIdPropertyKey = 660; private: uint32_t m_PropertyKey = Core::invalidPropertyKey; uint32_t m_Flags = 0; + uint32_t m_ConverterId = -1; public: inline uint32_t propertyKey() const { return m_PropertyKey; } @@ -57,11 +59,23 @@ flagsChanged(); } + inline uint32_t converterId() const { return m_ConverterId; } + void converterId(uint32_t value) + { + if (m_ConverterId == value) + { + return; + } + m_ConverterId = value; + converterIdChanged(); + } + Core* clone() const override; void copy(const DataBindBase& object) { m_PropertyKey = object.m_PropertyKey; m_Flags = object.m_Flags; + m_ConverterId = object.m_ConverterId; } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override @@ -74,6 +88,9 @@ case flagsPropertyKey: m_Flags = CoreUintType::deserialize(reader); return true; + case converterIdPropertyKey: + m_ConverterId = CoreUintType::deserialize(reader); + return true; } return false; } @@ -81,6 +98,7 @@ protected: virtual void propertyKeyChanged() {} virtual void flagsChanged() {} + virtual void converterIdChanged() {} }; } // namespace rive
diff --git a/include/rive/importers/backboard_importer.hpp b/include/rive/importers/backboard_importer.hpp index f237931..c92454f 100644 --- a/include/rive/importers/backboard_importer.hpp +++ b/include/rive/importers/backboard_importer.hpp
@@ -12,6 +12,8 @@ class Backboard; class FileAsset; class FileAssetReferencer; +class DataConverter; +class DataBind; class BackboardImporter : public ImportStackObject { private: @@ -20,6 +22,8 @@ std::vector<NestedArtboard*> m_NestedArtboards; std::vector<FileAsset*> m_FileAssets; std::vector<FileAssetReferencer*> m_FileAssetReferencers; + std::vector<DataConverter*> m_DataConverters; + std::vector<DataBind*> m_DataConverterReferencers; int m_NextArtboardId; public: @@ -29,6 +33,8 @@ void addNestedArtboard(NestedArtboard* artboard); void addFileAsset(FileAsset* asset); void addFileAssetReferencer(FileAssetReferencer* referencer); + void addDataConverterReferencer(DataBind* referencer); + void addDataConverter(DataConverter* converter); StatusCode resolve() override; const Backboard* backboard() const { return m_Backboard; }
diff --git a/src/data_bind/context/context_value.cpp b/src/data_bind/context/context_value.cpp new file mode 100644 index 0000000..5a12954 --- /dev/null +++ b/src/data_bind/context/context_value.cpp
@@ -0,0 +1,122 @@ +#include "rive/data_bind/context/context_value.hpp" +#include "rive/data_bind/context/context_value_color.hpp" +#include "rive/data_bind/data_values/data_type.hpp" +#include "rive/data_bind/data_values/data_value.hpp" +#include "rive/data_bind/data_values/data_value_number.hpp" +#include "rive/data_bind/data_values/data_value_string.hpp" +#include "rive/data_bind/data_values/data_value_enum.hpp" +#include "rive/data_bind/data_values/data_value_color.hpp" +#include "rive/data_bind/data_values/data_value_boolean.hpp" +#include "rive/generated/core_registry.hpp" + +using namespace rive; + +DataBindContextValue::DataBindContextValue(ViewModelInstanceValue* source, + DataConverter* converter) : + m_source(source), m_converter(converter) +{ + if (m_source != nullptr) + { + switch (m_source->coreType()) + { + case ViewModelInstanceNumberBase::typeKey: + m_dataValue = + new DataValueNumber(m_source->as<ViewModelInstanceNumber>()->propertyValue()); + break; + case ViewModelInstanceStringBase::typeKey: + m_dataValue = + new DataValueString(m_source->as<ViewModelInstanceString>()->propertyValue()); + break; + case ViewModelInstanceColorBase::typeKey: + m_dataValue = + new DataValueColor(m_source->as<ViewModelInstanceColor>()->propertyValue()); + break; + case ViewModelInstanceBooleanBase::typeKey: + m_dataValue = + new DataValueBoolean(m_source->as<ViewModelInstanceBoolean>()->propertyValue()); + break; + case ViewModelInstanceEnumBase::typeKey: + { + auto viewmodelInstanceEnum = m_source->as<ViewModelInstanceEnum>(); + auto viewModelPropertyEnum = + viewmodelInstanceEnum->viewModelProperty()->as<ViewModelPropertyEnum>(); + m_dataValue = new DataValueEnum(viewmodelInstanceEnum->propertyValue(), + viewModelPropertyEnum->dataEnum()); + } + break; + default: + m_dataValue = new DataValue(); + } + } +} + +void DataBindContextValue::updateSourceValue() +{ + if (m_source != nullptr) + { + switch (m_source->coreType()) + { + case ViewModelInstanceNumberBase::typeKey: + m_dataValue->as<DataValueNumber>()->value( + m_source->as<ViewModelInstanceNumber>()->propertyValue()); + break; + case ViewModelInstanceStringBase::typeKey: + m_dataValue->as<DataValueString>()->value( + m_source->as<ViewModelInstanceString>()->propertyValue()); + break; + case ViewModelInstanceColorBase::typeKey: + m_dataValue->as<DataValueColor>()->value( + m_source->as<ViewModelInstanceColor>()->propertyValue()); + break; + case ViewModelInstanceBooleanBase::typeKey: + m_dataValue->as<DataValueBoolean>()->value( + m_source->as<ViewModelInstanceBoolean>()->propertyValue()); + break; + case ViewModelInstanceEnumBase::typeKey: + m_dataValue->as<DataValueEnum>()->value( + m_source->as<ViewModelInstanceEnum>()->propertyValue()); + break; + } + } +} + +void DataBindContextValue::applyToSource(Core* component, + uint32_t propertyKey, + bool isMainDirection) +{ + auto targetValue = getTargetValue(component, propertyKey); + switch (m_source->coreType()) + { + case ViewModelInstanceNumberBase::typeKey: + { + + auto value = calculateValue<DataValueNumber, float>(targetValue, isMainDirection); + m_source->as<ViewModelInstanceNumber>()->propertyValue(value); + } + break; + case ViewModelInstanceStringBase::typeKey: + { + auto value = calculateValue<DataValueString, std::string>(targetValue, isMainDirection); + m_source->as<ViewModelInstanceString>()->propertyValue(value); + } + break; + case ViewModelInstanceColorBase::typeKey: + { + auto value = calculateValue<DataValueColor, int>(targetValue, isMainDirection); + m_source->as<ViewModelInstanceColor>()->propertyValue(value); + } + break; + case ViewModelInstanceBooleanBase::typeKey: + { + auto value = calculateValue<DataValueBoolean, bool>(targetValue, isMainDirection); + m_source->as<ViewModelInstanceBoolean>()->propertyValue(value); + } + break; + case ViewModelInstanceEnumBase::typeKey: + { + auto value = calculateValue<DataValueEnum, uint32_t>(targetValue, isMainDirection); + m_source->as<ViewModelInstanceEnum>()->propertyValue(value); + } + break; + } +} \ No newline at end of file
diff --git a/src/data_bind/context/context_value_boolean.cpp b/src/data_bind/context/context_value_boolean.cpp index b7b708b..b08b8f5 100644 --- a/src/data_bind/context/context_value_boolean.cpp +++ b/src/data_bind/context/context_value_boolean.cpp
@@ -1,27 +1,23 @@ #include "rive/data_bind/context/context_value_boolean.hpp" +#include "rive/data_bind/data_values/data_value_boolean.hpp" #include "rive/generated/core_registry.hpp" using namespace rive; -DataBindContextValueBoolean::DataBindContextValueBoolean(ViewModelInstanceValue* value) +DataBindContextValueBoolean::DataBindContextValueBoolean(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +void DataBindContextValueBoolean::apply(Core* target, uint32_t propertyKey, bool isMainDirection) { - m_Source = value; - m_Value = m_Source->as<ViewModelInstanceBoolean>()->propertyValue(); + updateSourceValue(); + auto value = calculateValue<DataValueBoolean, bool>(m_dataValue, isMainDirection); + CoreRegistry::setBool(target, propertyKey, value); } -void DataBindContextValueBoolean::apply(Core* target, uint32_t propertyKey) -{ - CoreRegistry::setBool(target, - propertyKey, - m_Source->as<ViewModelInstanceBoolean>()->propertyValue()); -} - -void DataBindContextValueBoolean::applyToSource(Core* target, uint32_t propertyKey) +DataValue* DataBindContextValueBoolean::getTargetValue(Core* target, uint32_t propertyKey) { auto value = CoreRegistry::getBool(target, propertyKey); - if (m_Value != value) - { - m_Value = value; - m_Source->as<ViewModelInstanceBoolean>()->propertyValue(value); - } + return new DataValueBoolean(value); } \ No newline at end of file
diff --git a/src/data_bind/context/context_value_color.cpp b/src/data_bind/context/context_value_color.cpp index 44a1857..c66171d 100644 --- a/src/data_bind/context/context_value_color.cpp +++ b/src/data_bind/context/context_value_color.cpp
@@ -1,27 +1,23 @@ #include "rive/data_bind/context/context_value_color.hpp" +#include "rive/data_bind/data_values/data_value_color.hpp" #include "rive/generated/core_registry.hpp" using namespace rive; -DataBindContextValueColor::DataBindContextValueColor(ViewModelInstanceValue* value) +DataBindContextValueColor::DataBindContextValueColor(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +void DataBindContextValueColor::apply(Core* target, uint32_t propertyKey, bool isMainDirection) { - m_Source = value; - m_Value = m_Source->as<ViewModelInstanceColor>()->propertyValue(); + updateSourceValue(); + auto value = calculateValue<DataValueColor, int>(m_dataValue, isMainDirection); + CoreRegistry::setColor(target, propertyKey, value); } -void DataBindContextValueColor::apply(Core* target, uint32_t propertyKey) -{ - CoreRegistry::setColor(target, - propertyKey, - m_Source->as<ViewModelInstanceColor>()->propertyValue()); -} - -void DataBindContextValueColor::applyToSource(Core* target, uint32_t propertyKey) +DataValue* DataBindContextValueColor::getTargetValue(Core* target, uint32_t propertyKey) { auto value = CoreRegistry::getColor(target, propertyKey); - if (m_Value != value) - { - m_Value = value; - m_Source->as<ViewModelInstanceColor>()->propertyValue(value); - } + return new DataValueColor(value); } \ No newline at end of file
diff --git a/src/data_bind/context/context_value_enum.cpp b/src/data_bind/context/context_value_enum.cpp index a0064bc..dac5920 100644 --- a/src/data_bind/context/context_value_enum.cpp +++ b/src/data_bind/context/context_value_enum.cpp
@@ -1,36 +1,27 @@ #include "rive/data_bind/context/context_value_enum.hpp" +#include "rive/data_bind/data_values/data_value_enum.hpp" #include "rive/generated/core_registry.hpp" using namespace rive; -DataBindContextValueEnum::DataBindContextValueEnum(ViewModelInstanceValue* value) +DataBindContextValueEnum::DataBindContextValueEnum(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +void DataBindContextValueEnum::apply(Core* target, uint32_t propertyKey, bool isMainDirection) { - m_Source = value; - m_Value = m_Source->as<ViewModelInstanceEnum>()->propertyValue(); + + updateSourceValue(); + auto value = calculateValue<DataValueEnum, uint32_t>(m_dataValue, isMainDirection); + CoreRegistry::setUint(target, propertyKey, value); } -void DataBindContextValueEnum::apply(Core* target, uint32_t propertyKey) +DataValue* DataBindContextValueEnum::getTargetValue(Core* target, uint32_t propertyKey) { - auto enumSource = m_Source->as<ViewModelInstanceEnum>(); - auto enumProperty = enumSource->viewModelProperty()->as<ViewModelPropertyEnum>(); - auto enumValue = enumProperty->value(m_Source->as<ViewModelInstanceEnum>()->propertyValue()); - // TODO: @hernan decide which one makes more sense. Probably setUint, but setString was used to - // update text input with enum values. - CoreRegistry::setString(target, propertyKey, enumValue); - CoreRegistry::setUint(target, - propertyKey, - m_Source->as<ViewModelInstanceEnum>()->propertyValue()); -} - -void DataBindContextValueEnum::applyToSource(Core* target, uint32_t propertyKey) -{ - auto value = CoreRegistry::getString(target, propertyKey); - auto enumSource = m_Source->as<ViewModelInstanceEnum>(); - auto enumProperty = enumSource->viewModelProperty()->as<ViewModelPropertyEnum>(); - auto valueIndex = enumProperty->valueIndex(value); - if (valueIndex != -1 && m_Value != valueIndex) - { - m_Value = valueIndex; - m_Source->as<ViewModelInstanceEnum>()->value(static_cast<uint32_t>(valueIndex)); - } + auto value = CoreRegistry::getUint(target, propertyKey); + auto viewmodelInstanceEnum = m_source->as<ViewModelInstanceEnum>(); + auto viewModelPropertyEnum = + viewmodelInstanceEnum->viewModelProperty()->as<ViewModelPropertyEnum>(); + return new DataValueEnum(value, viewModelPropertyEnum->dataEnum()); } \ No newline at end of file
diff --git a/src/data_bind/context/context_value_list.cpp b/src/data_bind/context/context_value_list.cpp index f0aa427..92fc2fd 100644 --- a/src/data_bind/context/context_value_list.cpp +++ b/src/data_bind/context/context_value_list.cpp
@@ -5,10 +5,10 @@ using namespace rive; -DataBindContextValueList::DataBindContextValueList(ViewModelInstanceValue* value) -{ - m_Source = value; -} +DataBindContextValueList::DataBindContextValueList(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} std::unique_ptr<ArtboardInstance> DataBindContextValueList::createArtboard( Component* target, @@ -72,7 +72,7 @@ { if (target != nullptr) { - auto sourceList = m_Source->as<ViewModelInstanceList>(); + auto sourceList = m_source->as<ViewModelInstanceList>(); auto listItems = sourceList->listItems(); int listIndex = 0; @@ -125,9 +125,11 @@ } } -void DataBindContextValueList::apply(Core* target, uint32_t propertyKey) {} +void DataBindContextValueList::apply(Core* target, uint32_t propertyKey, bool isMainDirection) {} -void DataBindContextValueList::applyToSource(Core* target, uint32_t propertyKey) +void DataBindContextValueList::applyToSource(Core* target, + uint32_t propertyKey, + bool isMainDirection) { // TODO: @hernan does applyToSource make sense? Should we block it somehow? } \ No newline at end of file
diff --git a/src/data_bind/context/context_value_number.cpp b/src/data_bind/context/context_value_number.cpp index 63fabf8..4f40997 100644 --- a/src/data_bind/context/context_value_number.cpp +++ b/src/data_bind/context/context_value_number.cpp
@@ -1,27 +1,23 @@ #include "rive/data_bind/context/context_value_number.hpp" +#include "rive/data_bind/data_values/data_value_number.hpp" #include "rive/generated/core_registry.hpp" using namespace rive; -DataBindContextValueNumber::DataBindContextValueNumber(ViewModelInstanceValue* value) +DataBindContextValueNumber::DataBindContextValueNumber(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +void DataBindContextValueNumber::apply(Core* target, uint32_t propertyKey, bool isMainDirection) { - m_Source = value; - m_Value = m_Source->as<ViewModelInstanceNumber>()->propertyValue(); + updateSourceValue(); + auto value = calculateValue<DataValueNumber, float>(m_dataValue, isMainDirection); + CoreRegistry::setDouble(target, propertyKey, value); } -void DataBindContextValueNumber::apply(Core* target, uint32_t propertyKey) -{ - CoreRegistry::setDouble(target, - propertyKey, - m_Source->as<ViewModelInstanceNumber>()->propertyValue()); -} - -void DataBindContextValueNumber::applyToSource(Core* target, uint32_t propertyKey) +DataValue* DataBindContextValueNumber::getTargetValue(Core* target, uint32_t propertyKey) { auto value = CoreRegistry::getDouble(target, propertyKey); - if (m_Value != value) - { - m_Value = value; - m_Source->as<ViewModelInstanceNumber>()->propertyValue(value); - } + return new DataValueNumber(value); } \ No newline at end of file
diff --git a/src/data_bind/context/context_value_string.cpp b/src/data_bind/context/context_value_string.cpp index 9cf19f7..2c73dc8 100644 --- a/src/data_bind/context/context_value_string.cpp +++ b/src/data_bind/context/context_value_string.cpp
@@ -1,27 +1,23 @@ #include "rive/data_bind/context/context_value_string.hpp" +#include "rive/data_bind/data_values/data_value_string.hpp" #include "rive/generated/core_registry.hpp" using namespace rive; -DataBindContextValueString::DataBindContextValueString(ViewModelInstanceValue* value) +DataBindContextValueString::DataBindContextValueString(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +void DataBindContextValueString::apply(Core* target, uint32_t propertyKey, bool isMainDirection) { - m_Source = value; - m_Value = m_Source->as<ViewModelInstanceString>()->propertyValue(); + updateSourceValue(); + auto value = calculateValue<DataValueString, std::string>(m_dataValue, isMainDirection); + CoreRegistry::setString(target, propertyKey, value); } -void DataBindContextValueString::apply(Core* target, uint32_t propertyKey) -{ - CoreRegistry::setString(target, - propertyKey, - m_Source->as<ViewModelInstanceString>()->propertyValue()); -} - -void DataBindContextValueString::applyToSource(Core* target, uint32_t propertyKey) +DataValue* DataBindContextValueString::getTargetValue(Core* target, uint32_t propertyKey) { auto value = CoreRegistry::getString(target, propertyKey); - if (m_Value != value) - { - m_Value = value; - m_Source->as<ViewModelInstanceString>()->propertyValue(value); - } + return new DataValueString(value); } \ No newline at end of file
diff --git a/src/data_bind/converters/data_converter.cpp b/src/data_bind/converters/data_converter.cpp new file mode 100644 index 0000000..d9645b9 --- /dev/null +++ b/src/data_bind/converters/data_converter.cpp
@@ -0,0 +1,18 @@ +#include "rive/data_bind/converters/data_converter.hpp" +#include "rive/importers/import_stack.hpp" +#include "rive/importers/backboard_importer.hpp" +#include "rive/backboard.hpp" + +using namespace rive; + +StatusCode DataConverter::import(ImportStack& importStack) +{ + auto backboardImporter = importStack.latest<BackboardImporter>(Backboard::typeKey); + if (backboardImporter == nullptr) + { + return StatusCode::MissingObject; + } + backboardImporter->addDataConverter(this); + + return Super::import(importStack); +} \ No newline at end of file
diff --git a/src/data_bind/data_bind.cpp b/src/data_bind/data_bind.cpp index 9877f5b..e4c52c9 100644 --- a/src/data_bind/data_bind.cpp +++ b/src/data_bind/data_bind.cpp
@@ -14,10 +14,12 @@ #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/data_values/data_type.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; @@ -34,6 +36,13 @@ StatusCode DataBind::import(ImportStack& importStack) { + + auto backboardImporter = importStack.latest<BackboardImporter>(Backboard::typeKey); + if (backboardImporter == nullptr) + { + return StatusCode::MissingObject; + } + backboardImporter->addDataConverterReferencer(this); if (target()) { switch (target()->coreType()) @@ -66,31 +75,61 @@ } } } + 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; + } + return DataType::none; +} + void DataBind::bind() { - switch (m_Source->coreType()) + switch (outputType()) { - case ViewModelInstanceNumberBase::typeKey: - m_ContextValue = rivestd::make_unique<DataBindContextValueNumber>(m_Source); + case DataType::number: + m_ContextValue = + rivestd::make_unique<DataBindContextValueNumber>(m_Source, converter()); break; - case ViewModelInstanceStringBase::typeKey: - m_ContextValue = rivestd::make_unique<DataBindContextValueString>(m_Source); + case DataType::string: + m_ContextValue = + rivestd::make_unique<DataBindContextValueString>(m_Source, converter()); break; - case ViewModelInstanceEnumBase::typeKey: - m_ContextValue = rivestd::make_unique<DataBindContextValueEnum>(m_Source); + case DataType::boolean: + m_ContextValue = + rivestd::make_unique<DataBindContextValueBoolean>(m_Source, converter()); break; - case ViewModelInstanceListBase::typeKey: - m_ContextValue = rivestd::make_unique<DataBindContextValueList>(m_Source); + case DataType::color: + m_ContextValue = rivestd::make_unique<DataBindContextValueColor>(m_Source, converter()); + break; + case DataType::enumType: + m_ContextValue = rivestd::make_unique<DataBindContextValueEnum>(m_Source, converter()); + break; + case DataType::list: + m_ContextValue = rivestd::make_unique<DataBindContextValueList>(m_Source, converter()); m_ContextValue->update(m_target); break; - case ViewModelInstanceColorBase::typeKey: - m_ContextValue = rivestd::make_unique<DataBindContextValueColor>(m_Source); - break; - case ViewModelInstanceBooleanBase::typeKey: - m_ContextValue = rivestd::make_unique<DataBindContextValueBoolean>(m_Source); + default: break; } } @@ -114,7 +153,10 @@ if (((flagsValue & DataBindFlags::Direction) == DataBindFlags::ToTarget) || ((flagsValue & DataBindFlags::TwoWay) == DataBindFlags::TwoWay)) { - m_ContextValue->apply(m_target, propertyKey()); + m_ContextValue->apply(m_target, + propertyKey(), + (flagsValue & DataBindFlags::Direction) == + DataBindFlags::ToTarget); } } } @@ -128,7 +170,10 @@ { if (m_ContextValue != nullptr) { - m_ContextValue->applyToSource(m_target, propertyKey()); + m_ContextValue->applyToSource(m_target, + propertyKey(), + (flagsValue & DataBindFlags::Direction) == + DataBindFlags::ToSource); } } }
diff --git a/src/importers/backboard_importer.cpp b/src/importers/backboard_importer.cpp index 286f133..845c536 100644 --- a/src/importers/backboard_importer.cpp +++ b/src/importers/backboard_importer.cpp
@@ -7,6 +7,8 @@ #include "rive/assets/file_asset.hpp" #include "rive/viewmodel/viewmodel.hpp" #include "rive/viewmodel/viewmodel_instance.hpp" +#include "rive/data_bind/converters/data_converter.hpp" +#include "rive/data_bind/data_bind.hpp" #include <unordered_set> using namespace rive; @@ -85,6 +87,25 @@ auto asset = m_FileAssets[index]; referencer->setAsset(asset); } + for (auto referencer : m_DataConverterReferencers) + { + auto index = (size_t)referencer->converterId(); + if (index >= m_DataConverters.size() || index < 0) + { + continue; + } + referencer->converter(m_DataConverters[index]); + } return StatusCode::Ok; +} + +void BackboardImporter::addDataConverter(DataConverter* dataConverter) +{ + m_DataConverters.push_back(dataConverter); +} + +void BackboardImporter::addDataConverterReferencer(DataBind* dataBind) +{ + m_DataConverterReferencers.push_back(dataBind); } \ No newline at end of file