| #ifdef WITH_RIVE_SCRIPTING |
| #include "rive/lua/rive_lua_libs.hpp" |
| #endif |
| #include "rive/assets/script_asset.hpp" |
| #include "rive/artboard.hpp" |
| #include "rive/file.hpp" |
| #include "rive/script_input_artboard.hpp" |
| #include "rive/animation/scripted_listener_action.hpp" |
| #include "rive/animation/scripted_transition_condition.hpp" |
| #include "rive/scripted/scripted_data_converter.hpp" |
| #include "rive/scripted/scripted_drawable.hpp" |
| #include "rive/scripted/scripted_layout.hpp" |
| #include "rive/scripted/scripted_path_effect.hpp" |
| #include "rive/scripted/scripted_object.hpp" |
| #include "rive/data_bind/data_bind.hpp" |
| |
| using namespace rive; |
| |
| ScriptedObject* ScriptedObject::from(Core* object) |
| { |
| switch (object->coreType()) |
| { |
| case ScriptedDataConverter::typeKey: |
| return object->as<ScriptedDataConverter>(); |
| case ScriptedDrawable::typeKey: |
| return object->as<ScriptedDrawable>(); |
| case ScriptedLayout::typeKey: |
| return object->as<ScriptedLayout>(); |
| case ScriptedPathEffect::typeKey: |
| return object->as<ScriptedPathEffect>(); |
| case ScriptedListenerAction::typeKey: |
| return object->as<ScriptedListenerAction>(); |
| case ScriptedTransitionCondition::typeKey: |
| return object->as<ScriptedTransitionCondition>(); |
| } |
| return nullptr; |
| } |
| |
| #ifdef WITH_RIVE_SCRIPTING |
| void ScriptedObject::setArtboardInput(std::string name, Artboard* artboard) |
| { |
| lua_State* L = state(); |
| if (L == nullptr || scriptAsset() == nullptr) |
| { |
| return; |
| } |
| rive_lua_pushRef(L, m_self); |
| auto artboardInstance = artboard->instance(); |
| artboardInstance->frameOrigin(false); |
| lua_newrive<ScriptedArtboard>(L, |
| L, |
| ref_rcp(scriptAsset()->file()), |
| std::move(artboardInstance), |
| nullptr, |
| dataContext()); |
| lua_setfield(L, -2, name.c_str()); |
| rive_lua_pop(L, 1); |
| addScriptedDirt(ComponentDirt::ScriptUpdate); |
| } |
| |
| void ScriptedObject::setBooleanInput(std::string name, bool value) |
| { |
| lua_State* L = state(); |
| if (L == nullptr) |
| { |
| return; |
| } |
| rive_lua_pushRef(L, m_self); |
| lua_pushboolean(L, value); |
| lua_setfield(L, -2, name.c_str()); |
| rive_lua_pop(L, 1); |
| addScriptedDirt(ComponentDirt::ScriptUpdate); |
| } |
| |
| void ScriptedObject::setNumberInput(std::string name, float value) |
| { |
| lua_State* L = state(); |
| if (L == nullptr) |
| { |
| return; |
| } |
| rive_lua_pushRef(L, m_self); |
| lua_pushnumber(L, value); |
| lua_setfield(L, -2, name.c_str()); |
| rive_lua_pop(L, 1); |
| addScriptedDirt(ComponentDirt::ScriptUpdate); |
| } |
| |
| void ScriptedObject::setIntegerInput(std::string name, int value) |
| { |
| lua_State* L = state(); |
| if (L == nullptr) |
| { |
| return; |
| } |
| rive_lua_pushRef(L, m_self); |
| lua_pushunsigned(L, value); |
| lua_setfield(L, -2, name.c_str()); |
| rive_lua_pop(L, 1); |
| addScriptedDirt(ComponentDirt::ScriptUpdate); |
| } |
| |
| void ScriptedObject::setStringInput(std::string name, std::string value) |
| { |
| lua_State* L = state(); |
| if (L == nullptr) |
| { |
| return; |
| } |
| rive_lua_pushRef(L, m_self); |
| lua_pushstring(L, value.c_str()); |
| lua_setfield(L, -2, name.c_str()); |
| rive_lua_pop(L, 1); |
| addScriptedDirt(ComponentDirt::ScriptUpdate); |
| } |
| |
| void ScriptedObject::setViewModelInput(std::string name, |
| ViewModelInstanceValue* value) |
| { |
| lua_State* L = state(); |
| if (L == nullptr) |
| { |
| return; |
| } |
| rive_lua_pushRef(L, m_self); |
| switch (value->coreType()) |
| { |
| case ViewModelInstanceViewModelBase::typeKey: |
| { |
| auto viewModel = value->as<ViewModelInstanceViewModel>(); |
| auto vmi = viewModel->referenceViewModelInstance(); |
| if (vmi == nullptr) |
| { |
| fprintf(stderr, |
| "riveLuaPushViewModelInstanceValue - passed in a " |
| "ViewModelInstanceViewModel with no associated " |
| "ViewModelInstance.\n"); |
| return; |
| } |
| |
| lua_newrive<ScriptedViewModel>(L, |
| L, |
| ref_rcp(vmi->viewModel()), |
| vmi); |
| break; |
| } |
| default: |
| break; |
| } |
| lua_setfield(L, -2, name.c_str()); |
| rive_lua_pop(L, 1); |
| addScriptedDirt(ComponentDirt::ScriptUpdate); |
| } |
| |
| void ScriptedObject::trigger(std::string name) |
| { |
| lua_State* L = state(); |
| if (L == nullptr) |
| { |
| return; |
| } |
| rive_lua_pushRef(L, m_self); |
| if (static_cast<lua_Type>(lua_getfield(L, -1, name.c_str())) != |
| LUA_TFUNCTION) |
| { |
| rive_lua_pop(L, 2); |
| return; |
| } |
| lua_pushvalue(L, -2); |
| rive_lua_pcall(L, 1, 0); |
| rive_lua_pop(L, 1); |
| addScriptedDirt(ComponentDirt::ScriptUpdate); |
| } |
| |
| bool ScriptedObject::scriptAdvance(float elapsedSeconds) |
| { |
| lua_State* L = state(); |
| if (!advances() || L == nullptr) |
| { |
| return false; |
| } |
| rive_lua_pushRef(L, m_self); |
| lua_getfield(L, -1, "advance"); |
| lua_pushvalue(L, -2); |
| lua_pushnumber(L, elapsedSeconds); |
| if (static_cast<lua_Status>(rive_lua_pcall(L, 2, 1)) != LUA_OK) |
| { |
| rive_lua_pop(L, 2); |
| return false; |
| } |
| bool result = lua_toboolean(L, -1); |
| rive_lua_pop(L, 2); |
| return result; |
| } |
| |
| void ScriptedObject::scriptUpdate() |
| { |
| lua_State* L = state(); |
| if (!updates() || L == nullptr) |
| { |
| return; |
| } |
| // Stack: [] |
| rive_lua_pushRef(L, m_self); |
| // Stack: [self] |
| lua_getfield(L, -1, "update"); |
| // Stack: [self, field] Swap self and field |
| lua_insert(L, -2); |
| // Stack: [field, self] |
| if (static_cast<lua_Status>(rive_lua_pcall(L, 1, 0)) != LUA_OK) |
| { |
| rive_lua_pop(L, 1); |
| } |
| } |
| |
| bool ScriptedObject::scriptInit(ScriptingVM* vm) |
| { |
| lua_State* L = vm ? vm->state() : nullptr; |
| // Clean up old references if reinitializing |
| if (m_vm != nullptr) |
| { |
| lua_State* oldState = state(); |
| if (m_self != 0) |
| { |
| lua_unref(oldState, m_self); |
| m_self = 0; |
| } |
| disposeScriptedContext(); |
| } |
| for (auto prop : m_customProperties) |
| { |
| auto scriptInput = ScriptInput::from(prop); |
| if (scriptInput && !scriptInput->validateForScriptInit()) |
| { |
| rive_lua_pop(L, 1); |
| return false; |
| } |
| } |
| if (static_cast<lua_Status>(rive_lua_pcall(L, 0, 1)) != LUA_OK) |
| { |
| rive_lua_pop(L, 1); |
| return false; |
| } |
| if (static_cast<lua_Type>(lua_type(L, -1)) != LUA_TTABLE) |
| { |
| rive_lua_pop(L, 1); |
| return false; |
| } |
| else |
| { |
| // Stack: [self] |
| lua_newrive<ScriptedContext>(L, this); |
| // Stack: [self, ScriptedContext] |
| m_context = lua_ref(L, -1); |
| rive_lua_pop(L, 1); |
| // Stack: [self] |
| m_self = lua_ref(L, -1); |
| #ifdef WITH_RIVE_TOOLS |
| m_vm = ref_rcp(vm); // Increment refcount for shared ownership |
| #else |
| m_vm = vm; |
| #endif |
| for (auto prop : m_customProperties) |
| { |
| auto scriptInput = ScriptInput::from(prop); |
| if (scriptInput) |
| { |
| scriptInput->initScriptedValue(); |
| } |
| } |
| if (inits()) |
| { |
| // Stack: [self] |
| lua_getfield(L, -1, "init"); |
| // Stack: [self, field] |
| lua_pushvalue(L, -2); |
| // Stack: [self, field, self] |
| rive_lua_pushRef(L, m_context); |
| // Stack: [self, field, self, ScriptedContext] |
| auto pCallResult = rive_lua_pcall(L, 2, 1); |
| if (static_cast<lua_Status>(pCallResult) != LUA_OK) |
| { |
| lua_unref(L, m_self); |
| disposeScriptedContext(); |
| // Stack: [self, status] |
| rive_lua_pop(L, 2); |
| m_vm = nullptr; |
| m_self = 0; |
| return false; |
| } |
| if (!lua_toboolean(L, -1)) |
| { |
| lua_unref(L, m_self); |
| disposeScriptedContext(); |
| // Pop boolean and self table |
| // Stack: [self, result] |
| rive_lua_pop(L, 2); |
| m_vm = nullptr; |
| m_self = 0; |
| return false; |
| } |
| else |
| { |
| // Stack: [self, result] |
| rive_lua_pop(L, 1); |
| assert(static_cast<lua_Type>(lua_type(L, -1)) == LUA_TTABLE); |
| // Stack: [self] |
| rive_lua_pop(L, 1); |
| } |
| } |
| else |
| { |
| // Init function doesn't exist, just pop the self table |
| // Stack: [self] |
| rive_lua_pop(L, 1); |
| } |
| } |
| return true; |
| } |
| |
| void ScriptedObject::disposeScriptInputs() |
| { |
| for (auto prop : m_customProperties) |
| { |
| auto scriptInput = ScriptInput::from(prop); |
| if (scriptInput != nullptr) |
| { |
| scriptInput->scriptedObject(nullptr); |
| } |
| } |
| m_customProperties.clear(); |
| } |
| |
| void ScriptedObject::disposeScriptedContext() |
| { |
| if (m_context != 0) |
| { |
| lua_State* L = state(); |
| rive_lua_pushRef(L, m_context); |
| auto scriptedContext = lua_torive<ScriptedContext>(L, -1); |
| scriptedContext->dispose(); |
| rive_lua_pop(L, 1); |
| lua_unref(L, m_context); |
| m_context = 0; |
| } |
| } |
| |
| void ScriptedObject::scriptDispose() |
| { |
| disposeScriptInputs(); |
| |
| lua_State* L = state(); |
| if (L != nullptr) |
| { |
| lua_unref(L, m_self); |
| disposeScriptedContext(); |
| #ifdef TESTING |
| // Force GC to collect any ScriptedArtboard instances created via |
| // instance() |
| lua_gc(L, LUA_GCCOLLECT, 0); |
| #endif |
| } |
| m_vm = nullptr; |
| m_self = 0; |
| } |
| #else |
| void ScriptedObject::setArtboardInput(std::string name, Artboard* artboard) {} |
| |
| void ScriptedObject::setBooleanInput(std::string name, bool value) {} |
| |
| void ScriptedObject::setIntegerInput(std::string name, int value) {} |
| |
| void ScriptedObject::setNumberInput(std::string name, float value) {} |
| |
| void ScriptedObject::setStringInput(std::string name, std::string value) {} |
| |
| void ScriptedObject::setViewModelInput(std::string name, |
| ViewModelInstanceValue* value) |
| {} |
| |
| void ScriptedObject::trigger(std::string name) {} |
| |
| bool ScriptedObject::scriptAdvance(float elapsedSeconds) { return false; } |
| |
| void ScriptedObject::scriptUpdate() {} |
| |
| void ScriptedObject::scriptDispose() {} |
| |
| void ScriptedObject::disposeScriptInputs() {} |
| #endif |
| |
| void ScriptedObject::reinit() |
| { |
| if (scriptAsset() != nullptr) |
| { |
| scriptAsset()->initScriptedObject(this); |
| } |
| } |
| |
| ScriptAsset* ScriptedObject::scriptAsset() const |
| { |
| return (ScriptAsset*)m_fileAsset.get(); |
| } |
| |
| void ScriptedObject::setAsset(rcp<FileAsset> asset) |
| { |
| if (asset != nullptr && asset->is<ScriptAsset>()) |
| { |
| FileAssetReferencer::setAsset(asset); |
| } |
| } |
| |
| void ScriptedObject::markNeedsUpdate() {} |
| |
| void ScriptedObject::cloneProperties(CustomPropertyContainer* twin, |
| DataBindContainer* dataBindContainer) const |
| { |
| |
| for (auto prop : m_customProperties) |
| { |
| auto clonedValue = prop->clone()->as<CustomProperty>(); |
| twin->addProperty(clonedValue); |
| auto input = ScriptInput::from(prop); |
| if (input != nullptr) |
| { |
| auto dataBind = input->dataBind(); |
| if (dataBind) |
| { |
| auto dataBindClone = static_cast<DataBind*>(dataBind->clone()); |
| dataBindClone->file(dataBind->file()); |
| if (dataBind->converter() != nullptr) |
| { |
| dataBindClone->converter( |
| dataBind->converter()->clone()->as<DataConverter>()); |
| } |
| dataBindClone->target(clonedValue); |
| dataBindContainer->addDataBind(dataBindClone); |
| } |
| } |
| } |
| } |