| #ifndef _RIVE_SCRIPTED_OBJECT_HPP_ | |
| #define _RIVE_SCRIPTED_OBJECT_HPP_ | |
| #include "rive/assets/file_asset_referencer.hpp" | |
| #include "rive/assets/script_asset.hpp" | |
| #include "rive/custom_property.hpp" | |
| #include "rive/custom_property_container.hpp" | |
| #include "rive/refcnt.hpp" | |
| #include "rive/generated/assets/script_asset_base.hpp" | |
| #ifdef WITH_RIVE_SCRIPTING | |
| #include "rive/scripted/script_backend.hpp" | |
| #endif | |
| #include <algorithm> | |
| #include <vector> | |
| namespace rive | |
| { | |
| class Artboard; | |
| class Component; | |
| class DataContext; | |
| class ViewModelInstanceValue; | |
| class DataBindContainer; | |
| class ScriptedProperty; | |
| class ScriptedContext; | |
| class ScriptedObject : public FileAssetReferencer, | |
| public CustomPropertyContainer, | |
| public OptionalScriptedMethods | |
| { | |
| protected: | |
| int m_self = 0; | |
| int m_context = 0; | |
| ScriptedContext* m_contextPtr = nullptr; | |
| virtual void disposeScriptInputs(); | |
| public: | |
| /// The backend announces display scale changes here; layouts re-run | |
| /// their resize callback so scripts see the new scale. | |
| virtual void displayScaleChanged() {} | |
| protected: | |
| #ifdef WITH_RIVE_SCRIPTING | |
| // Non-owning. The backend tracks every ScriptedObject that points at it | |
| // (via registerScriptedObject in ensureScriptInitialized) and nulls these | |
| // out at teardown. Holding it owned would create a cycle: | |
| // ScriptingVM → lua_State → ScriptedArtboard userdata → | |
| // ScriptReffedArtboard → inner ArtboardInstance → ScriptedObject → | |
| // rcp<ScriptingVM>. | |
| ScriptBackend* m_vm = nullptr; | |
| friend class ScriptBackend; | |
| friend class ScriptingVM; | |
| friend class WasmScriptingVM; | |
| #endif | |
| bool inUpdatePhase() const { return m_inUpdatePhase; } | |
| void setInUpdatePhase(bool value) { m_inUpdatePhase = value; } | |
| private: | |
| rcp<DataContext> m_dataContext = nullptr; | |
| std::vector<ScriptedProperty*> m_trackedScriptedProperties; | |
| bool m_inUpdatePhase = false; | |
| #ifdef WITH_RIVE_SCRIPTING | |
| bool m_userLuaInitDone = false; | |
| bool tryUserInit(); | |
| #endif | |
| void disposeScriptedContext(); | |
| void disposeTrackedProperties(); | |
| public: | |
| virtual ~ScriptedObject() { scriptDispose(); } | |
| ScriptAsset* scriptAsset() const; | |
| void setArtboardInput(std::string name, Artboard* artboard); | |
| void setBooleanInput(std::string name, bool value); | |
| void setIntegerInput(std::string name, int value); | |
| void setNumberInput(std::string name, float value); | |
| void setStringInput(std::string name, std::string value); | |
| void setViewModelInput(std::string name, ViewModelInstanceValue* value); | |
| void trigger(std::string name); | |
| bool scriptAdvance(float elapsedSeconds); | |
| void scriptUpdate(); | |
| void reinit(); | |
| #ifdef WITH_RIVE_SCRIPTING | |
| bool userLuaInitDone() { return m_userLuaInitDone; } | |
| void resetLuaInit() { m_userLuaInitDone = false; } | |
| #else | |
| bool userLuaInitDone() { return true; } | |
| void resetLuaInit() {} | |
| #endif | |
| virtual void markNeedsUpdate(); | |
| virtual rcp<DataContext> dataContext() { return m_dataContext; } | |
| void dataContext(rcp<DataContext> value) { m_dataContext = value; } | |
| #ifdef WITH_RIVE_SCRIPTING | |
| /// Run the generator into m_self once per backend; does not push inputs | |
| /// or call init(); use hydrateScriptInputs() after data bind. | |
| bool ensureScriptInitialized(ScriptBackend* vm, int generatorRef); | |
| /// Resolve inputs from DataContext and push into the script; runs init() | |
| /// once after first successful hydration when implemented. | |
| bool hydrateScriptInputs(); | |
| ScriptBackend* backend() const { return m_vm; } | |
| #else | |
| bool hydrateScriptInputs() { return true; } | |
| #endif | |
| void scriptDispose(); | |
| virtual bool addScriptedDirt(ComponentDirt value, bool recurse = false) = 0; | |
| void setAsset(rcp<FileAsset> asset) override; | |
| static ScriptedObject* from(Core* object); | |
| virtual ScriptProtocol scriptProtocol() = 0; | |
| int self() { return m_self; } | |
| virtual Component* component() = 0; | |
| virtual ScriptedObject* cloneScriptedObject(DataBindContainer*) const | |
| { | |
| return nullptr; | |
| } | |
| void cloneProperties(CustomPropertyContainer*, DataBindContainer*) const; | |
| void addTrackedScriptedProperty(ScriptedProperty* property) | |
| { | |
| if (property != nullptr) | |
| { | |
| m_trackedScriptedProperties.push_back(property); | |
| } | |
| } | |
| void removeTrackedScriptedProperty(ScriptedProperty* property) | |
| { | |
| auto it = std::remove(m_trackedScriptedProperties.begin(), | |
| m_trackedScriptedProperties.end(), | |
| property); | |
| m_trackedScriptedProperties.erase(it, | |
| m_trackedScriptedProperties.end()); | |
| } | |
| const std::vector<ScriptedProperty*>& trackedScriptedProperties() const | |
| { | |
| return m_trackedScriptedProperties; | |
| } | |
| virtual bool addDataBindFromScriptedObject(DataBind*) { return false; } | |
| #ifdef WITH_RIVE_SCRIPTING | |
| /// Called after hydrateScriptInputs() succeeds; | |
| virtual void didHydrateScriptInputs() {} | |
| // A reinit replaced script state, like a VM swap on editor pause; hosts | |
| // re-record content the old state produced. | |
| virtual void didReinit() {} | |
| #endif | |
| }; | |
| } // namespace rive | |
| #endif |