| #ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_HPP_ | |
| #define _RIVE_DATA_BIND_CONTEXT_VALUE_HPP_ | |
| #include "rive/viewmodel/viewmodel_instance_value.hpp" | |
| #include "rive/data_bind/converters/data_converter.hpp" | |
| #include "rive/data_bind/data_bind.hpp" | |
| #include "rive/data_bind/data_values/data_value_number.hpp" | |
| #include "rive/data_bind/context/context_target_value.hpp" | |
| #include <stdio.h> | |
| namespace rive | |
| { | |
| class DataBindContextValue | |
| { | |
| protected: | |
| DataValue* m_dataValue = nullptr; | |
| DataBindContextTargetValue m_targetValue; | |
| bool m_isValid = false; | |
| public: | |
| DataBindContextValue(DataBind* dataBind); | |
| virtual ~DataBindContextValue() | |
| { | |
| if (m_dataValue != nullptr) | |
| { | |
| delete m_dataValue; | |
| } | |
| }; | |
| virtual void applyToSource(Core* component, | |
| uint32_t propertyKey, | |
| bool isMainDirection, | |
| DataBind* dataBind); | |
| virtual void apply(Core* component, | |
| uint32_t propertyKey, | |
| bool isMainDirection, | |
| DataBind* dataBind) {}; | |
| void invalidate() { m_isValid = false; }; | |
| // Re-read the target after we wrote it ourselves (source->target). The | |
| // cached target value is what detects target-side changes; if we leave it | |
| // holding the value from before our own write, a later genuine target | |
| // change that lands back on that stale value reads as "unchanged" and | |
| // never propagates to the source. Only toSource binds allocate the cache. | |
| void refreshTargetValue(DataBind* dataBind) | |
| { | |
| if (dataBind->toSource()) | |
| { | |
| m_targetValue.syncTargetValue(dataBind); | |
| } | |
| } | |
| virtual bool syncTargetValue(Core* target, uint32_t propertyKey) | |
| { | |
| return false; | |
| }; | |
| void syncSourceValue(DataBind* dataBind); | |
| DataValue* calculateUntypedDataValue(DataValue* input, | |
| bool isMainDirection, | |
| DataBind* dataBind) | |
| { | |
| auto converter = dataBind->converter(); | |
| auto dataValue = converter != nullptr | |
| ? isMainDirection | |
| ? converter->convert(input, dataBind) | |
| : converter->reverseConvert(input, dataBind) | |
| : input; | |
| return dataValue; | |
| } | |
| template <typename T = DataValue> | |
| DataValue* calculateDataValue(DataValue* input, | |
| bool isMainDirection, | |
| DataBind* dataBind) | |
| { | |
| auto dataValue = | |
| calculateUntypedDataValue(input, isMainDirection, dataBind); | |
| if (dataValue->is<T>()) | |
| { | |
| return dataValue; | |
| } | |
| return nullptr; | |
| }; | |
| template <typename T = DataValue, typename U> | |
| U calculateValue(DataValue* input, bool isMainDirection, DataBind* dataBind) | |
| { | |
| auto dataValue = | |
| calculateDataValue<T>(input, isMainDirection, dataBind); | |
| if (dataValue && dataValue->template is<T>()) | |
| { | |
| return dataValue->template as<T>()->value(); | |
| } | |
| return T::defaultValue; | |
| }; | |
| template <typename T = DataValue, | |
| typename U, | |
| typename V = ViewModelInstanceValue> | |
| void calculateValueAndApply(bool isMainDirection, DataBind* dataBind) | |
| { | |
| // Check if target value changed or binding has been invalidated | |
| if (m_targetValue.syncTargetValue(dataBind) || !m_isValid) | |
| { | |
| // Calculate new value after converters are applied | |
| auto value = calculateDataValue<T>(m_targetValue.dataValue(), | |
| isMainDirection, | |
| dataBind); | |
| if (value) | |
| { | |
| // Apply value to source | |
| dataBind->suppressDirt(true); | |
| auto source = dataBind->source(); | |
| source->as<V>()->applyValue(value->template as<T>()); | |
| dataBind->suppressDirt(false); | |
| m_isValid = true; | |
| } | |
| } | |
| }; | |
| }; | |
| } // namespace rive | |
| #endif |