| #include "rive/assets/script_asset.hpp" | |
| #include "rive/component_dirt.hpp" | |
| #include "rive/data_bind/data_bind.hpp" | |
| #include "rive/data_bind/data_bind_context.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_boolean.hpp" | |
| #include "rive/data_bind/data_values/data_value_color.hpp" | |
| #include "rive/data_bind/data_values/data_value_number.hpp" | |
| #include "rive/data_bind/data_values/data_value_string.hpp" | |
| #include "rive/scripted/scripted_data_converter.hpp" | |
| using namespace rive; | |
| ScriptedDataConverter::~ScriptedDataConverter() | |
| { | |
| disposeScriptInputs(); | |
| if (m_dataValue) | |
| { | |
| delete m_dataValue; | |
| } | |
| } | |
| void ScriptedDataConverter::disposeScriptInputs() | |
| { | |
| auto props = m_customProperties; | |
| ScriptedObject::disposeScriptInputs(); | |
| for (auto prop : props) | |
| { | |
| auto scriptInput = ScriptInput::from(prop); | |
| if (scriptInput != nullptr) | |
| { | |
| // ScriptedDataConverters need to delete their own inputs | |
| // because they are not components | |
| delete scriptInput; | |
| } | |
| } | |
| } | |
| #ifdef WITH_RIVE_SCRIPTING | |
| void ScriptedDataConverter::didHydrateScriptInputs() | |
| { | |
| addScriptedDirt(ComponentDirt::Bindings); | |
| } | |
| DataValue* ScriptedDataConverter::applyConversion(DataValue* value, | |
| const std::string& method) | |
| { | |
| if (m_vm == nullptr || !m_vm->valid() || m_self == 0) | |
| { | |
| return value; | |
| } | |
| ScriptBackend::ScriptDataResult result; | |
| if (!m_vm->callDataConvert(this, m_self, method.c_str(), value, &result)) | |
| { | |
| // Assumed for legacy files but not implemented; pass the value through | |
| // unchanged (same as !dataConverts()). | |
| return value; | |
| } | |
| switch (result.kind) | |
| { | |
| case ScriptBackend::ScriptDataResult::Kind::number: | |
| storeData<DataValueNumber>(result.number); | |
| break; | |
| case ScriptBackend::ScriptDataResult::Kind::string: | |
| storeData<DataValueString>(result.string); | |
| break; | |
| case ScriptBackend::ScriptDataResult::Kind::boolean: | |
| storeData<DataValueBoolean>(result.boolean); | |
| break; | |
| case ScriptBackend::ScriptDataResult::Kind::color: | |
| storeData<DataValueColor>(result.color); | |
| break; | |
| case ScriptBackend::ScriptDataResult::Kind::none: | |
| break; | |
| } | |
| if (!m_dataValue) | |
| { | |
| m_dataValue = new DataValue(); | |
| } | |
| return m_dataValue; | |
| } | |
| DataValue* ScriptedDataConverter::convert(DataValue* value, DataBind* dataBind) | |
| { | |
| if (dataConverts()) | |
| { | |
| return applyConversion(value, "convert"); | |
| } | |
| return value; | |
| } | |
| DataValue* ScriptedDataConverter::reverseConvert(DataValue* value, | |
| DataBind* dataBind) | |
| { | |
| if (dataReverseConverts()) | |
| { | |
| return applyConversion(value, "reverseConvert"); | |
| } | |
| return value; | |
| } | |
| #endif | |
| void ScriptedDataConverter::bindFromContext(DataContext* dataContext, | |
| DataBind* dataBind) | |
| { | |
| m_dataContext = rcp<DataContext>(safe_ref(dataContext)); | |
| Super::bindFromContext(dataContext, dataBind); | |
| reinit(); | |
| for (auto prop : m_customProperties) | |
| { | |
| auto input = ScriptInput::from(prop); | |
| if (input != nullptr) | |
| { | |
| if (input->dataBind() != nullptr) | |
| { | |
| input->dataBind()->as<DataBindContext>()->bindFromContext( | |
| dataContext); | |
| } | |
| } | |
| } | |
| } | |
| bool ScriptedDataConverter::advanceComponent(float elapsedSeconds, | |
| AdvanceFlags flags) | |
| { | |
| if (!enums::is_flag_set(flags, AdvanceFlags::AdvanceNested)) | |
| { | |
| elapsedSeconds = 0; | |
| } | |
| return advance(elapsedSeconds); | |
| } | |
| bool ScriptedDataConverter::advance(float elapsedSeconds) | |
| { | |
| if (elapsedSeconds == 0) | |
| { | |
| return false; | |
| } | |
| auto needsAdvance = scriptAdvance(elapsedSeconds); | |
| if (needsAdvance) | |
| { | |
| markConverterDirty(); | |
| } | |
| return needsAdvance; | |
| } | |
| void ScriptedDataConverter::addProperty(CustomProperty* prop) | |
| { | |
| auto scriptInput = ScriptInput::from(prop); | |
| if (scriptInput != nullptr) | |
| { | |
| scriptInput->scriptedObject(this); | |
| } | |
| CustomPropertyContainer::addProperty(prop); | |
| } | |
| StatusCode ScriptedDataConverter::import(ImportStack& importStack) | |
| { | |
| auto result = registerReferencer(importStack); | |
| if (result != StatusCode::Ok) | |
| { | |
| return result; | |
| } | |
| return Super::import(importStack); | |
| } | |
| Core* ScriptedDataConverter::clone() const | |
| { | |
| ScriptedDataConverter* twin = | |
| ScriptedDataConverterBase::clone()->as<ScriptedDataConverter>(); | |
| if (m_fileAsset != nullptr) | |
| { | |
| twin->setAsset(m_fileAsset); | |
| } | |
| for (auto prop : m_customProperties) | |
| { | |
| auto clonedValue = prop->clone()->as<CustomProperty>(); | |
| twin->addProperty(clonedValue); | |
| auto scriptedInputClone = ScriptInput::from(clonedValue); | |
| auto scriptedInputSource = ScriptInput::from(prop); | |
| auto thisDataBinds = dataBinds(); | |
| auto twinDataBinds = twin->dataBinds(); | |
| if (scriptedInputClone && scriptedInputSource) | |
| { | |
| if (scriptedInputSource->dataBind()) | |
| { | |
| int index = 0; | |
| // Data binds are cloned in the data converters, and assigned to | |
| // the right target here | |
| for (auto& dataBind : thisDataBinds) | |
| { | |
| if (dataBind->target() == prop) | |
| { | |
| if (index < twinDataBinds.size()) | |
| { | |
| twinDataBinds[index]->target(clonedValue); | |
| scriptedInputClone->dataBind(twinDataBinds[index]); | |
| } | |
| } | |
| index++; | |
| } | |
| } | |
| } | |
| } | |
| return twin; | |
| } | |
| bool ScriptedDataConverter::addDataBindFromScriptedObject(DataBind* dataBind) | |
| { | |
| addDataBind(dataBind); | |
| return true; | |
| } |