mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
Scripting in Core Runtime (#11235) 0a5325e474
rev to luau 0.702 (#11259) b50983c49d Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com> Co-authored-by: Philip Chung <philterdesign@gmail.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
08a3949a0d8d393b113be39d80e278c8eac10d0e
|
||||
0a5325e474d689a7f18d4ea260513ed86b4bf1cc
|
||||
|
||||
@@ -37,8 +37,17 @@
|
||||
},
|
||||
"description": "The ref of the generator function (the function returned by the compiled code).",
|
||||
"coop": false,
|
||||
"withRiveToolsOnly": true,
|
||||
"exportsToRuntimeConditionally": true,
|
||||
"journal": false
|
||||
},
|
||||
"isModule": {
|
||||
"type": "bool",
|
||||
"initialValue": "false",
|
||||
"key": {
|
||||
"int": 914,
|
||||
"string": "ismodule"
|
||||
},
|
||||
"description": "Whether the script is a module (doesn't implement a protocol like Node, Layout, etc."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ namespace rive
|
||||
class Artboard;
|
||||
class Component;
|
||||
class DataBind;
|
||||
class File;
|
||||
class ScriptedObject;
|
||||
|
||||
enum ScriptProtocol
|
||||
@@ -35,6 +34,7 @@ protected:
|
||||
DataBind* m_dataBind = nullptr;
|
||||
|
||||
public:
|
||||
virtual ~ScriptInput() {};
|
||||
virtual void initScriptedValue();
|
||||
virtual bool validateForScriptInit() = 0;
|
||||
static ScriptInput* from(Core* component);
|
||||
@@ -61,8 +61,7 @@ private:
|
||||
int m_implementedMethods = 0;
|
||||
|
||||
protected:
|
||||
bool verifyImplementation(ScriptProtocol scriptProtocol,
|
||||
LuaState* luaState);
|
||||
bool verifyImplementation(ScriptedObject* object, LuaState* luaState);
|
||||
|
||||
public:
|
||||
int implementedMethods() { return m_implementedMethods; }
|
||||
@@ -104,7 +103,19 @@ public:
|
||||
bool inits() { return (m_implementedMethods & m_initsBit) != 0; }
|
||||
};
|
||||
|
||||
class ScriptAsset : public ScriptAssetBase, public OptionalScriptedMethods
|
||||
class ModuleDetails
|
||||
{
|
||||
public:
|
||||
virtual ~ModuleDetails() = default;
|
||||
virtual std::string moduleName() = 0;
|
||||
virtual void registrationComplete(int ref) {}
|
||||
virtual Span<uint8_t> moduleBytecode() { return Span<uint8_t>(); }
|
||||
virtual bool isProtocolScript() = 0;
|
||||
};
|
||||
|
||||
class ScriptAsset : public ScriptAssetBase,
|
||||
public OptionalScriptedMethods,
|
||||
public ModuleDetails
|
||||
{
|
||||
|
||||
public:
|
||||
@@ -112,7 +123,7 @@ public:
|
||||
friend class ScriptAssetImporter;
|
||||
|
||||
bool verified() const { return m_verified; }
|
||||
Span<uint8_t> bytecode() { return m_bytecode; }
|
||||
Span<uint8_t> moduleBytecode() override { return m_bytecode; }
|
||||
#endif
|
||||
|
||||
bool initScriptedObject(ScriptedObject* object);
|
||||
@@ -129,23 +140,19 @@ public:
|
||||
void file(File* value) { m_file = value; }
|
||||
File* file() const { return m_file; }
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
LuaState* vm()
|
||||
{
|
||||
if (m_file == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return m_file->scriptingVM();
|
||||
}
|
||||
LuaState* vm();
|
||||
void registrationComplete(int ref) override;
|
||||
#endif
|
||||
std::string moduleName() override { return name(); }
|
||||
bool isProtocolScript() override { return !isModule(); }
|
||||
|
||||
private:
|
||||
File* m_file = nullptr;
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
bool m_scriptRegistered = false;
|
||||
bool m_verified = false;
|
||||
SimpleArray<uint8_t> m_bytecode;
|
||||
bool m_initted = false;
|
||||
ScriptProtocol m_scriptProtocol = ScriptProtocol::utility;
|
||||
#endif
|
||||
|
||||
bool initScriptedObjectWith(ScriptedObject* object);
|
||||
|
||||
@@ -15,6 +15,7 @@ protected:
|
||||
|
||||
public:
|
||||
virtual void addProperty(CustomProperty* prop);
|
||||
virtual void removeProperty(CustomProperty* prop);
|
||||
virtual const std::vector<Component*>& containerChildren() const
|
||||
{
|
||||
static const std::vector<Component*> emptyVec;
|
||||
|
||||
@@ -31,6 +31,10 @@ class Factory;
|
||||
class ScrollPhysics;
|
||||
class ViewModelRuntime;
|
||||
class BindableArtboard;
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
class CPPRuntimeScriptingContext;
|
||||
class ScriptingVM;
|
||||
#endif
|
||||
|
||||
///
|
||||
/// Tracks the success/failure result when importing a Rive file.
|
||||
@@ -162,8 +166,27 @@ public:
|
||||
|
||||
std::vector<Artboard*> artboards() { return m_artboards; };
|
||||
|
||||
void scriptingVM(LuaState* vm) { m_luaState = vm; }
|
||||
LuaState* scriptingVM() { return m_luaState; }
|
||||
// When the runtime is hosted in the editor, we get a pointer
|
||||
// to the VM that we can use. If this is nullptr, we can assume
|
||||
// we are running in the runtime and should instance our own VMs
|
||||
// and pass them down to the root
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
void scriptingVM(LuaState* vm)
|
||||
{
|
||||
cleanupScriptingVM();
|
||||
m_luaState = vm;
|
||||
}
|
||||
LuaState* scriptingVM()
|
||||
{
|
||||
// For now, if we don't have a vm, create one. In the future, we
|
||||
// may need a way to create multiple vms in parallel
|
||||
if (m_luaState == nullptr)
|
||||
{
|
||||
makeScriptingVM();
|
||||
}
|
||||
return m_luaState;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
/// Strips FileAssetContents for FileAssets of given typeKeys.
|
||||
@@ -221,7 +244,14 @@ private:
|
||||
/// with the file.
|
||||
rcp<FileAssetLoader> m_assetLoader;
|
||||
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
LuaState* m_luaState = nullptr;
|
||||
std::unique_ptr<CPPRuntimeScriptingContext> m_scriptingContext;
|
||||
std::unique_ptr<ScriptingVM> m_scriptingVM;
|
||||
void makeScriptingVM();
|
||||
void cleanupScriptingVM();
|
||||
void registerScripts();
|
||||
#endif
|
||||
|
||||
rcp<ViewModelInstance> copyViewModelInstance(
|
||||
ViewModelInstance* viewModelInstance,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef _RIVE_SCRIPT_ASSET_BASE_HPP_
|
||||
#define _RIVE_SCRIPT_ASSET_BASE_HPP_
|
||||
#include "rive/assets/file_asset.hpp"
|
||||
#include "rive/core/field_types/core_bool_type.hpp"
|
||||
#include "rive/core/field_types/core_uint_type.hpp"
|
||||
namespace rive
|
||||
{
|
||||
@@ -29,17 +30,14 @@ public:
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
static const uint16_t generatorFunctionRefPropertyKey = 893;
|
||||
#endif
|
||||
static const uint16_t isModulePropertyKey = 914;
|
||||
|
||||
protected:
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
uint32_t m_GeneratorFunctionRef = 0;
|
||||
#endif
|
||||
bool m_IsModule = false;
|
||||
|
||||
public:
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
inline uint32_t generatorFunctionRef() const
|
||||
{
|
||||
return m_GeneratorFunctionRef;
|
||||
@@ -53,14 +51,23 @@ public:
|
||||
m_GeneratorFunctionRef = value;
|
||||
generatorFunctionRefChanged();
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool isModule() const { return m_IsModule; }
|
||||
void isModule(bool value)
|
||||
{
|
||||
if (m_IsModule == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_IsModule = value;
|
||||
isModuleChanged();
|
||||
}
|
||||
|
||||
Core* clone() const override;
|
||||
void copy(const ScriptAssetBase& object)
|
||||
{
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
m_GeneratorFunctionRef = object.m_GeneratorFunctionRef;
|
||||
#endif
|
||||
m_IsModule = object.m_IsModule;
|
||||
FileAsset::copy(object);
|
||||
}
|
||||
|
||||
@@ -68,19 +75,19 @@ public:
|
||||
{
|
||||
switch (propertyKey)
|
||||
{
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
case generatorFunctionRefPropertyKey:
|
||||
m_GeneratorFunctionRef = CoreUintType::deserialize(reader);
|
||||
return true;
|
||||
#endif
|
||||
case isModulePropertyKey:
|
||||
m_IsModule = CoreBoolType::deserialize(reader);
|
||||
return true;
|
||||
}
|
||||
return FileAsset::deserialize(propertyKey, reader);
|
||||
}
|
||||
|
||||
protected:
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
virtual void generatorFunctionRefChanged() {}
|
||||
#endif
|
||||
virtual void isModuleChanged() {}
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
|
||||
@@ -1508,11 +1508,9 @@ public:
|
||||
case FileAssetBase::assetIdPropertyKey:
|
||||
object->as<FileAssetBase>()->assetId(value);
|
||||
break;
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
case ScriptAssetBase::generatorFunctionRefPropertyKey:
|
||||
object->as<ScriptAssetBase>()->generatorFunctionRef(value);
|
||||
break;
|
||||
#endif
|
||||
case AudioEventBase::assetIdPropertyKey:
|
||||
object->as<AudioEventBase>()->assetId(value);
|
||||
break;
|
||||
@@ -1754,6 +1752,9 @@ public:
|
||||
case TextBase::fitFromBaselinePropertyKey:
|
||||
object->as<TextBase>()->fitFromBaseline(value);
|
||||
break;
|
||||
case ScriptAssetBase::isModulePropertyKey:
|
||||
object->as<ScriptAssetBase>()->isModule(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
static void setDouble(Core* object, int propertyKey, float value)
|
||||
@@ -2940,10 +2941,8 @@ public:
|
||||
return object->as<CustomPropertyEnumBase>()->enumId();
|
||||
case FileAssetBase::assetIdPropertyKey:
|
||||
return object->as<FileAssetBase>()->assetId();
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
case ScriptAssetBase::generatorFunctionRefPropertyKey:
|
||||
return object->as<ScriptAssetBase>()->generatorFunctionRef();
|
||||
#endif
|
||||
case AudioEventBase::assetIdPropertyKey:
|
||||
return object->as<AudioEventBase>()->assetId();
|
||||
case ScriptInputArtboardBase::artboardIdPropertyKey:
|
||||
@@ -3123,6 +3122,8 @@ public:
|
||||
return object->as<TextFollowPathModifierBase>()->orient();
|
||||
case TextBase::fitFromBaselinePropertyKey:
|
||||
return object->as<TextBase>()->fitFromBaseline();
|
||||
case ScriptAssetBase::isModulePropertyKey:
|
||||
return object->as<ScriptAssetBase>()->isModule();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -3804,9 +3805,7 @@ public:
|
||||
case CustomPropertyEnumBase::propertyValuePropertyKey:
|
||||
case CustomPropertyEnumBase::enumIdPropertyKey:
|
||||
case FileAssetBase::assetIdPropertyKey:
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
case ScriptAssetBase::generatorFunctionRefPropertyKey:
|
||||
#endif
|
||||
case AudioEventBase::assetIdPropertyKey:
|
||||
case ScriptInputArtboardBase::artboardIdPropertyKey:
|
||||
return CoreUintType::id;
|
||||
@@ -3883,6 +3882,7 @@ public:
|
||||
case TextFollowPathModifierBase::radialPropertyKey:
|
||||
case TextFollowPathModifierBase::orientPropertyKey:
|
||||
case TextBase::fitFromBaselinePropertyKey:
|
||||
case ScriptAssetBase::isModulePropertyKey:
|
||||
return CoreBoolType::id;
|
||||
case ViewModelInstanceNumberBase::propertyValuePropertyKey:
|
||||
case CustomPropertyNumberBase::propertyValuePropertyKey:
|
||||
@@ -4559,10 +4559,8 @@ public:
|
||||
return object->is<CustomPropertyEnumBase>();
|
||||
case FileAssetBase::assetIdPropertyKey:
|
||||
return object->is<FileAssetBase>();
|
||||
#ifdef WITH_RIVE_TOOLS
|
||||
case ScriptAssetBase::generatorFunctionRefPropertyKey:
|
||||
return object->is<ScriptAssetBase>();
|
||||
#endif
|
||||
case AudioEventBase::assetIdPropertyKey:
|
||||
return object->is<AudioEventBase>();
|
||||
case ScriptInputArtboardBase::artboardIdPropertyKey:
|
||||
@@ -4709,6 +4707,8 @@ public:
|
||||
return object->is<TextFollowPathModifierBase>();
|
||||
case TextBase::fitFromBaselinePropertyKey:
|
||||
return object->is<TextBase>();
|
||||
case ScriptAssetBase::isModulePropertyKey:
|
||||
return object->is<ScriptAssetBase>();
|
||||
case ViewModelInstanceNumberBase::propertyValuePropertyKey:
|
||||
return object->is<ViewModelInstanceNumberBase>();
|
||||
case CustomPropertyNumberBase::propertyValuePropertyKey:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#define _RIVE_LUA_LIBS_HPP_
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "rive/assets/script_asset.hpp"
|
||||
#include "rive/lua/lua_state.hpp"
|
||||
#include "rive/math/raw_path.hpp"
|
||||
#include "rive/renderer.hpp"
|
||||
@@ -26,13 +27,14 @@
|
||||
#include "rive/data_bind/data_values/data_value_number.hpp"
|
||||
#include "rive/data_bind/data_values/data_value_string.hpp"
|
||||
#include "rive/viewmodel/viewmodel.hpp"
|
||||
#include "rive/artboard.hpp"
|
||||
#include "rive/file.hpp"
|
||||
#include "rive/animation/state_machine_instance.hpp"
|
||||
#include "rive/hit_result.hpp"
|
||||
#include "rive/refcnt.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
static const int maxCStack = 8000;
|
||||
static const int luaGlobalsIndex = -maxCStack - 2002;
|
||||
@@ -40,7 +42,14 @@ static const int luaRegistryIndex = -maxCStack - 2000;
|
||||
|
||||
namespace rive
|
||||
{
|
||||
class Artboard;
|
||||
class ArtboardInstance;
|
||||
class Factory;
|
||||
class File;
|
||||
class ModuleDetails;
|
||||
class ScriptedObject;
|
||||
class StateMachineInstance;
|
||||
class TransformComponent;
|
||||
enum class LuaAtoms : int16_t
|
||||
{
|
||||
// Vector
|
||||
@@ -423,9 +432,9 @@ public:
|
||||
std::unique_ptr<ArtboardInstance>&& artboardInstance);
|
||||
|
||||
~ScriptReffedArtboard();
|
||||
rive::rcp<rive::File> file() { return m_file; }
|
||||
Artboard* artboard() { return m_artboard.get(); }
|
||||
StateMachineInstance* stateMachine() { return m_stateMachine.get(); }
|
||||
rive::rcp<rive::File> file();
|
||||
Artboard* artboard();
|
||||
StateMachineInstance* stateMachine();
|
||||
rcp<ViewModelInstance> viewModelInstance() { return m_viewModelInstance; }
|
||||
|
||||
private:
|
||||
@@ -440,6 +449,7 @@ class ScriptedArtboard
|
||||
public:
|
||||
ScriptedArtboard(rcp<File> file,
|
||||
std::unique_ptr<ArtboardInstance>&& artboardInstance);
|
||||
~ScriptedArtboard();
|
||||
|
||||
static constexpr uint8_t luaTag = LUA_T_COUNT + 10;
|
||||
static constexpr const char* luaName = "Artboard";
|
||||
@@ -465,6 +475,8 @@ public:
|
||||
|
||||
bool advance(float seconds);
|
||||
|
||||
void cleanupDataRef(lua_State* L);
|
||||
|
||||
private:
|
||||
rcp<ScriptReffedArtboard> m_scriptReffedArtboard;
|
||||
int m_dataRef = 0;
|
||||
@@ -716,8 +728,27 @@ public:
|
||||
virtual void printEndLine() = 0;
|
||||
virtual int pCall(lua_State* state, int nargs, int nresults) = 0;
|
||||
|
||||
void queuePendingModule(ModuleDetails* moduleDetails);
|
||||
void clearPendingModule(const std::string& name);
|
||||
|
||||
// Add a module to be registered later via performRegistration()
|
||||
void addModule(ModuleDetails* moduleDetails);
|
||||
|
||||
// Perform registration of all added modules, handling dependencies and
|
||||
// retries
|
||||
void performRegistration(lua_State* state);
|
||||
|
||||
private:
|
||||
bool tryRegisterModule(lua_State* state,
|
||||
ModuleDetails* moduleDetails,
|
||||
int& functionRef);
|
||||
|
||||
void retryPendingModules(lua_State* state);
|
||||
|
||||
private:
|
||||
Factory* m_factory;
|
||||
std::vector<ModuleDetails*> m_pendingModules;
|
||||
std::vector<ModuleDetails*> m_modulesToRegister;
|
||||
};
|
||||
|
||||
class ScriptingVM
|
||||
@@ -730,6 +761,17 @@ public:
|
||||
lua_State* state() { return m_state; }
|
||||
|
||||
static void init(lua_State* state, ScriptingContext* context);
|
||||
|
||||
// Add a module to be registered later via performRegistration()
|
||||
void addModule(ModuleDetails* moduleDetails)
|
||||
{
|
||||
m_context->addModule(moduleDetails);
|
||||
}
|
||||
|
||||
// Perform registration of all added modules, handling dependencies and
|
||||
// retries
|
||||
void performRegistration() { m_context->performRegistration(m_state); }
|
||||
|
||||
static bool registerModule(lua_State* state,
|
||||
const char* name,
|
||||
Span<uint8_t> bytecode);
|
||||
@@ -742,6 +784,7 @@ public:
|
||||
Span<uint8_t> bytecode);
|
||||
|
||||
bool registerScript(const char* name, Span<uint8_t> bytecode);
|
||||
|
||||
static void dumpStack(lua_State* state);
|
||||
|
||||
private:
|
||||
@@ -909,6 +952,7 @@ class CPPRuntimeScriptingContext : public ScriptingContext
|
||||
{
|
||||
public:
|
||||
CPPRuntimeScriptingContext(Factory* factory) : ScriptingContext(factory) {}
|
||||
virtual ~CPPRuntimeScriptingContext() = default;
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> executionTime;
|
||||
|
||||
|
||||
@@ -11,9 +11,10 @@ class Artboard;
|
||||
class ScriptInputArtboard : public ScriptInputArtboardBase, public ScriptInput
|
||||
{
|
||||
private:
|
||||
Artboard* m_artboard;
|
||||
Artboard* m_artboard = nullptr;
|
||||
|
||||
public:
|
||||
~ScriptInputArtboard();
|
||||
void artboard(Artboard* artboard) { m_artboard = artboard; }
|
||||
void initScriptedValue() override
|
||||
{
|
||||
@@ -31,6 +32,7 @@ public:
|
||||
bool validateForScriptInit() override { return m_artboard != nullptr; }
|
||||
StatusCode import(ImportStack& importStack) override;
|
||||
Core* clone() const override;
|
||||
StatusCode onAddedClean(CoreContext* context) override;
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
|
||||
@@ -8,7 +8,11 @@ namespace rive
|
||||
{
|
||||
class ScriptInputBoolean : public ScriptInputBooleanBase, public ScriptInput
|
||||
{
|
||||
protected:
|
||||
void propertyValueChanged() override;
|
||||
|
||||
public:
|
||||
~ScriptInputBoolean();
|
||||
void initScriptedValue() override
|
||||
{
|
||||
ScriptInput::initScriptedValue();
|
||||
@@ -20,6 +24,7 @@ public:
|
||||
}
|
||||
bool validateForScriptInit() override { return true; }
|
||||
StatusCode import(ImportStack& importStack) override;
|
||||
StatusCode onAddedClean(CoreContext* context) override;
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
|
||||
@@ -8,7 +8,11 @@ namespace rive
|
||||
{
|
||||
class ScriptInputColor : public ScriptInputColorBase, public ScriptInput
|
||||
{
|
||||
protected:
|
||||
void propertyValueChanged() override;
|
||||
|
||||
public:
|
||||
~ScriptInputColor();
|
||||
void initScriptedValue() override
|
||||
{
|
||||
ScriptInput::initScriptedValue();
|
||||
@@ -20,6 +24,7 @@ public:
|
||||
}
|
||||
bool validateForScriptInit() override { return true; }
|
||||
StatusCode import(ImportStack& importStack) override;
|
||||
StatusCode onAddedClean(CoreContext* context) override;
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
|
||||
@@ -8,7 +8,11 @@ namespace rive
|
||||
{
|
||||
class ScriptInputNumber : public ScriptInputNumberBase, public ScriptInput
|
||||
{
|
||||
protected:
|
||||
void propertyValueChanged() override;
|
||||
|
||||
public:
|
||||
~ScriptInputNumber();
|
||||
void initScriptedValue() override
|
||||
{
|
||||
ScriptInput::initScriptedValue();
|
||||
@@ -18,6 +22,7 @@ public:
|
||||
obj->setNumberInput(name(), propertyValue());
|
||||
}
|
||||
}
|
||||
StatusCode onAddedClean(CoreContext* context) override;
|
||||
bool validateForScriptInit() override { return true; }
|
||||
StatusCode import(ImportStack& importStack) override;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,11 @@ namespace rive
|
||||
{
|
||||
class ScriptInputString : public ScriptInputStringBase, public ScriptInput
|
||||
{
|
||||
protected:
|
||||
void propertyValueChanged() override;
|
||||
|
||||
public:
|
||||
~ScriptInputString();
|
||||
void initScriptedValue() override
|
||||
{
|
||||
ScriptInput::initScriptedValue();
|
||||
@@ -20,6 +24,7 @@ public:
|
||||
}
|
||||
bool validateForScriptInit() override { return true; }
|
||||
StatusCode import(ImportStack& importStack) override;
|
||||
StatusCode onAddedClean(CoreContext* context) override;
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
|
||||
@@ -8,14 +8,14 @@ namespace rive
|
||||
{
|
||||
class ScriptInputTrigger : public ScriptInputTriggerBase, public ScriptInput
|
||||
{
|
||||
protected:
|
||||
void propertyValueChanged() override;
|
||||
|
||||
public:
|
||||
~ScriptInputTrigger();
|
||||
bool validateForScriptInit() override { return true; }
|
||||
void fire(const CallbackData& value) override
|
||||
{
|
||||
Super::fire(value);
|
||||
scriptedObject()->trigger(name());
|
||||
}
|
||||
StatusCode import(ImportStack& importStack) override;
|
||||
StatusCode onAddedClean(CoreContext* context) override;
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ protected:
|
||||
std::vector<uint32_t> m_DataBindPathIdsBuffer;
|
||||
|
||||
public:
|
||||
~ScriptInputViewModelProperty();
|
||||
void decodeDataBindPathIds(Span<const uint8_t> value) override;
|
||||
void copyDataBindPathIds(
|
||||
const ScriptInputViewModelPropertyBase& object) override;
|
||||
@@ -25,6 +26,7 @@ public:
|
||||
void initScriptedValue() override;
|
||||
bool validateForScriptInit() override;
|
||||
StatusCode import(ImportStack& importStack) override;
|
||||
StatusCode onAddedClean(CoreContext* context) override;
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ private:
|
||||
}
|
||||
m_dataValue->as<T>()->value(input->as<T>()->value());
|
||||
};
|
||||
virtual void disposeScriptInputs() override;
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
DataValue* applyConversion(DataValue* value, const std::string& method);
|
||||
#endif
|
||||
@@ -51,6 +52,7 @@ public:
|
||||
AdvanceFlags flags = AdvanceFlags::Animate |
|
||||
AdvanceFlags::NewFrame) override;
|
||||
StatusCode import(ImportStack& importStack) override;
|
||||
void addProperty(CustomProperty* prop) override;
|
||||
Core* clone() const override;
|
||||
bool addScriptedDirt(ComponentDirt value, bool recurse = false) override
|
||||
{
|
||||
|
||||
@@ -40,6 +40,7 @@ public:
|
||||
{
|
||||
return children();
|
||||
}
|
||||
void addProperty(CustomProperty* prop) override;
|
||||
bool addScriptedDirt(ComponentDirt value, bool recurse = false) override;
|
||||
DataContext* dataContext() override
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
LayoutScaleType widthScaleType,
|
||||
LayoutScaleType heightScaleType,
|
||||
LayoutDirection direction) override;
|
||||
void addProperty(CustomProperty* prop) override;
|
||||
Core* clone() const override;
|
||||
ScriptProtocol scriptProtocol() override { return ScriptProtocol::layout; }
|
||||
};
|
||||
|
||||
@@ -24,11 +24,13 @@ class ScriptedObject : public FileAssetReferencer,
|
||||
protected:
|
||||
int m_self = 0;
|
||||
int m_context = 0;
|
||||
virtual void disposeScriptInputs();
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
LuaState* m_state = nullptr;
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual ~ScriptedObject() { scriptDispose(); }
|
||||
ScriptAsset* scriptAsset() const;
|
||||
void setArtboardInput(std::string name, Artboard* artboard);
|
||||
void setBooleanInput(std::string name, bool value);
|
||||
|
||||
@@ -17,6 +17,7 @@ public:
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
bool scriptInit(LuaState* state) override;
|
||||
#endif
|
||||
void addProperty(CustomProperty* prop) override;
|
||||
StatusCode onAddedClean(CoreContext* context) override;
|
||||
StatusCode onAddedDirty(CoreContext* context) override;
|
||||
uint32_t assetId() override { return scriptAssetId(); }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
local dependency = require('dependency')
|
||||
local luau = dependency.github('luigi-rosso/luau', 'rive_0_28')
|
||||
local luau = dependency.github('luigi-rosso/luau', 'rive_0_29')
|
||||
local libhydrogen = dependency.github('luigi-rosso/libhydrogen', 'rive_0_1')
|
||||
|
||||
dofile('rive_build_config.lua')
|
||||
|
||||
@@ -42,9 +42,8 @@ ScriptInput* ScriptInput::from(Core* component)
|
||||
|
||||
void ScriptInput::initScriptedValue() {}
|
||||
|
||||
bool OptionalScriptedMethods::verifyImplementation(
|
||||
ScriptProtocol scriptProtocol,
|
||||
LuaState* luaState)
|
||||
bool OptionalScriptedMethods::verifyImplementation(ScriptedObject* object,
|
||||
LuaState* luaState)
|
||||
{
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
auto state = luaState->state;
|
||||
@@ -63,6 +62,7 @@ bool OptionalScriptedMethods::verifyImplementation(
|
||||
}
|
||||
m_implementedMethods = 0;
|
||||
|
||||
auto scriptProtocol = object->scriptProtocol();
|
||||
if (scriptProtocol == ScriptProtocol::node ||
|
||||
scriptProtocol == ScriptProtocol::layout ||
|
||||
scriptProtocol == ScriptProtocol::converter ||
|
||||
@@ -148,6 +148,19 @@ bool OptionalScriptedMethods::verifyImplementation(
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
LuaState* ScriptAsset::vm()
|
||||
{
|
||||
if (m_file == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
// We get the scripting VM from File for now, however,
|
||||
// this will need to change if/when we support multiple VMs
|
||||
return m_file->scriptingVM();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ScriptAsset::initScriptedObject(ScriptedObject* object)
|
||||
{
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
@@ -161,26 +174,47 @@ bool ScriptAsset::initScriptedObject(ScriptedObject* object)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(WITH_RIVE_SCRIPTING)
|
||||
void ScriptAsset::registrationComplete(int ref)
|
||||
{
|
||||
if (isModule())
|
||||
{
|
||||
m_scriptRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
generatorFunctionRef(ref);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ScriptAsset::initScriptedObjectWith(ScriptedObject* object)
|
||||
{
|
||||
#if defined(WITH_RIVE_SCRIPTING) && defined(WITH_RIVE_TOOLS)
|
||||
if (vm() == nullptr || generatorFunctionRef() == 0)
|
||||
#if defined(WITH_RIVE_SCRIPTING)
|
||||
if (vm() == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto vmState = vm()->state;
|
||||
auto pushedType = rive_lua_pushRef(vmState, generatorFunctionRef());
|
||||
if (static_cast<lua_Type>(pushedType) != LUA_TFUNCTION)
|
||||
if (generatorFunctionRef() == 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"ScriptAsset::initScriptedObjectWith: did not push a function "
|
||||
"at generatorFunctionRef, instead it pushed a %d\n ",
|
||||
pushedType);
|
||||
"ScriptAsset doesn't have a generator function %s\n",
|
||||
name().c_str());
|
||||
return false;
|
||||
}
|
||||
auto vmState = vm()->state;
|
||||
rive_lua_pushRef(vmState, generatorFunctionRef());
|
||||
|
||||
if (!m_initted)
|
||||
{
|
||||
m_scriptProtocol = object->scriptProtocol();
|
||||
verifyImplementation(m_scriptProtocol, vm());
|
||||
if (!verifyImplementation(object, vm()))
|
||||
{
|
||||
fprintf(stderr,
|
||||
"ScriptAsset failed to verify method implementation %s\n",
|
||||
name().c_str());
|
||||
rive_lua_pop(vmState, 1);
|
||||
return false;
|
||||
}
|
||||
m_initted = true;
|
||||
}
|
||||
object->implementedMethods(implementedMethods());
|
||||
|
||||
@@ -24,4 +24,14 @@ void CustomPropertyContainer::addProperty(CustomProperty* prop)
|
||||
{
|
||||
m_customProperties.push_back(prop);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomPropertyContainer::removeProperty(CustomProperty* prop)
|
||||
{
|
||||
auto it =
|
||||
std::find(m_customProperties.begin(), m_customProperties.end(), prop);
|
||||
if (it != m_customProperties.end())
|
||||
{
|
||||
m_customProperties.erase(it);
|
||||
}
|
||||
}
|
||||
61
src/file.cpp
61
src/file.cpp
@@ -17,6 +17,9 @@
|
||||
#include "rive/importers/file_asset_importer.hpp"
|
||||
#include "rive/importers/script_asset_importer.hpp"
|
||||
#include "rive/importers/import_stack.hpp"
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
#include "rive/lua/rive_lua_libs.hpp"
|
||||
#endif
|
||||
#include "rive/importers/keyed_object_importer.hpp"
|
||||
#include "rive/importers/keyed_property_importer.hpp"
|
||||
#include "rive/importers/linear_animation_importer.hpp"
|
||||
@@ -220,6 +223,9 @@ File::~File()
|
||||
delete physics;
|
||||
}
|
||||
delete m_backboard;
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
cleanupScriptingVM();
|
||||
#endif
|
||||
}
|
||||
|
||||
rcp<File> File::import(Span<const uint8_t> bytes,
|
||||
@@ -569,11 +575,64 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header)
|
||||
}
|
||||
}
|
||||
|
||||
return !reader.hasError() && importStack.resolve() == StatusCode::Ok
|
||||
auto resolved = importStack.resolve();
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
registerScripts();
|
||||
#endif
|
||||
return !reader.hasError() && resolved == StatusCode::Ok
|
||||
? ImportResult::success
|
||||
: ImportResult::malformed;
|
||||
}
|
||||
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
void File::registerScripts()
|
||||
{
|
||||
if (m_scriptingVM == nullptr)
|
||||
{
|
||||
makeScriptingVM();
|
||||
}
|
||||
|
||||
// Add all scripts to the VM for registration
|
||||
for (auto asset : m_fileAssets)
|
||||
{
|
||||
if (asset->is<ScriptAsset>())
|
||||
{
|
||||
ScriptAsset* scriptAsset = asset->as<ScriptAsset>();
|
||||
// At runtime, generatorFunctionRef should be 0, meaning
|
||||
// it hasn't been registered yet with a VM.
|
||||
if (scriptAsset->verified())
|
||||
{
|
||||
m_scriptingVM->addModule(scriptAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform registration - ScriptingContext will handle dependencies and
|
||||
// retries
|
||||
m_scriptingVM->performRegistration();
|
||||
}
|
||||
|
||||
void File::makeScriptingVM()
|
||||
{
|
||||
cleanupScriptingVM();
|
||||
m_scriptingContext =
|
||||
rivestd::make_unique<CPPRuntimeScriptingContext>(m_factory);
|
||||
m_scriptingVM = rivestd::make_unique<ScriptingVM>(m_scriptingContext.get());
|
||||
m_luaState = new LuaState(m_scriptingVM->state());
|
||||
}
|
||||
|
||||
void File::cleanupScriptingVM()
|
||||
{
|
||||
if (m_scriptingVM != nullptr)
|
||||
{
|
||||
delete m_luaState;
|
||||
m_luaState = nullptr;
|
||||
m_scriptingVM.reset();
|
||||
m_scriptingContext.reset();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Artboard* File::artboard(std::string name) const
|
||||
{
|
||||
for (const auto& artboard : m_artboards)
|
||||
|
||||
@@ -16,7 +16,6 @@ void ScriptedObjectImporter::addInput(CustomProperty* value)
|
||||
if (input != nullptr)
|
||||
{
|
||||
m_scriptedObject->addProperty(value);
|
||||
input->scriptedObject(m_scriptedObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
#include "rive/lua/rive_lua_libs.hpp"
|
||||
#include "rive/file.hpp"
|
||||
#include "rive/artboard.hpp"
|
||||
#include "rive/animation/state_machine_instance.hpp"
|
||||
#include "rive/viewmodel/viewmodel_property_number.hpp"
|
||||
#include "rive/viewmodel/viewmodel_property_trigger.hpp"
|
||||
#include "rive/node.hpp"
|
||||
@@ -33,6 +36,15 @@ ScriptReffedArtboard::~ScriptReffedArtboard()
|
||||
m_file = nullptr;
|
||||
}
|
||||
|
||||
rive::rcp<rive::File> ScriptReffedArtboard::file() { return m_file; }
|
||||
|
||||
Artboard* ScriptReffedArtboard::artboard() { return m_artboard.get(); }
|
||||
|
||||
StateMachineInstance* ScriptReffedArtboard::stateMachine()
|
||||
{
|
||||
return m_stateMachine.get();
|
||||
}
|
||||
|
||||
static int artboard_draw(lua_State* L)
|
||||
{
|
||||
auto scriptedArtboard = lua_torive<ScriptedArtboard>(L, 1);
|
||||
@@ -299,6 +311,17 @@ ScriptedArtboard::ScriptedArtboard(
|
||||
make_rcp<ScriptReffedArtboard>(file, std::move(artboardInstance)))
|
||||
{}
|
||||
|
||||
ScriptedArtboard::~ScriptedArtboard() { m_scriptReffedArtboard = nullptr; }
|
||||
|
||||
void ScriptedArtboard::cleanupDataRef(lua_State* L)
|
||||
{
|
||||
if (m_dataRef != 0)
|
||||
{
|
||||
lua_unref(L, m_dataRef);
|
||||
m_dataRef = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int node_index(lua_State* L)
|
||||
{
|
||||
int atom;
|
||||
@@ -539,9 +562,22 @@ static int node_namecall(lua_State* L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scripted_artboard_dtor(lua_State* L, void* data)
|
||||
{
|
||||
ScriptedArtboard* artboard = static_cast<ScriptedArtboard*>(data);
|
||||
// Clean up m_dataRef before calling the C++ destructor
|
||||
// (we can't do this in ~ScriptedArtboard() because it doesn't have access
|
||||
// to lua_State*)
|
||||
artboard->cleanupDataRef(L);
|
||||
// Call the C++ destructor
|
||||
artboard->~ScriptedArtboard();
|
||||
}
|
||||
|
||||
int luaopen_rive_artboards(lua_State* L)
|
||||
{
|
||||
lua_register_rive<ScriptedArtboard>(L);
|
||||
// Override the default destructor to clean up m_dataRef first
|
||||
lua_setuserdatadtor(L, ScriptedArtboard::luaTag, scripted_artboard_dtor);
|
||||
|
||||
lua_pushcfunction(L, artboard_index, nullptr);
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#ifdef WITH_RIVE_SCRIPTING
|
||||
#include "rive/lua/rive_lua_libs.hpp"
|
||||
#include "rive/assets/script_asset.hpp"
|
||||
#include "lualib.h"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
|
||||
using namespace rive;
|
||||
@@ -446,10 +448,148 @@ static void dump_stack(lua_State* state)
|
||||
|
||||
void ScriptingVM::dumpStack(lua_State* state) { dump_stack(state); }
|
||||
|
||||
void ScriptingContext::addModule(ModuleDetails* moduleDetails)
|
||||
{
|
||||
m_modulesToRegister.push_back(moduleDetails);
|
||||
}
|
||||
|
||||
bool ScriptingContext::tryRegisterModule(lua_State* state,
|
||||
ModuleDetails* moduleDetails,
|
||||
int& functionRef)
|
||||
{
|
||||
const std::string& name = moduleDetails->moduleName();
|
||||
functionRef = 0;
|
||||
if (moduleDetails->isProtocolScript())
|
||||
{
|
||||
if (ScriptingVM::registerScript(state,
|
||||
name.c_str(),
|
||||
moduleDetails->moduleBytecode()))
|
||||
{
|
||||
// registerScript leaves the function on the stack
|
||||
if (static_cast<lua_Type>(lua_type(state, -1)) == LUA_TFUNCTION)
|
||||
{
|
||||
functionRef = lua_ref(state, -1);
|
||||
}
|
||||
lua_pop(state, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ScriptingVM::registerModule(state,
|
||||
name.c_str(),
|
||||
moduleDetails->moduleBytecode()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptingContext::retryPendingModules(lua_State* state)
|
||||
{
|
||||
bool anyRetried = true;
|
||||
while (anyRetried)
|
||||
{
|
||||
anyRetried = false;
|
||||
// Track which modules we've tried in this iteration to avoid infinite
|
||||
// loops
|
||||
std::unordered_set<std::string> triedThisCycle;
|
||||
auto currentPending = m_pendingModules;
|
||||
|
||||
for (ModuleDetails* moduleDetails : currentPending)
|
||||
{
|
||||
const std::string& name = moduleDetails->moduleName();
|
||||
|
||||
// Skip if we've already tried this module in this iteration
|
||||
if (triedThisCycle.find(name) != triedThisCycle.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
triedThisCycle.insert(name);
|
||||
|
||||
int functionRef = 0;
|
||||
if (tryRegisterModule(state, moduleDetails, functionRef))
|
||||
{
|
||||
// Successfully registered, remove from pending list
|
||||
clearPendingModule(name);
|
||||
|
||||
moduleDetails->registrationComplete(
|
||||
moduleDetails->isProtocolScript() ? functionRef : 0);
|
||||
|
||||
anyRetried = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingContext::performRegistration(lua_State* state)
|
||||
{
|
||||
// Try to register all modules
|
||||
std::unordered_set<std::string> tried;
|
||||
|
||||
bool anyRegistered = true;
|
||||
while (anyRegistered)
|
||||
{
|
||||
anyRegistered = false;
|
||||
|
||||
for (ModuleDetails* moduleDetails : m_modulesToRegister)
|
||||
{
|
||||
const std::string& name = moduleDetails->moduleName();
|
||||
|
||||
// Skip if already registered
|
||||
if (checkRegisteredModules(state, name.c_str()) == 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if we've already tried this module
|
||||
if (tried.find(name) != tried.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tried.insert(name);
|
||||
|
||||
// Try to register the module
|
||||
int functionRef = 0;
|
||||
if (tryRegisterModule(state, moduleDetails, functionRef))
|
||||
{
|
||||
// Successfully registered
|
||||
anyRegistered = true;
|
||||
|
||||
moduleDetails->registrationComplete(
|
||||
moduleDetails->isProtocolScript() ? functionRef : 0);
|
||||
|
||||
this->retryPendingModules(state);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Registration failed - likely missing dependencies
|
||||
// Queue for retry
|
||||
queuePendingModule(moduleDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the modules list after registration attempt
|
||||
m_modulesToRegister.clear();
|
||||
}
|
||||
|
||||
bool ScriptingVM::registerScript(lua_State* state,
|
||||
const char* name,
|
||||
Span<uint8_t> bytecode)
|
||||
{
|
||||
// Check if already registered
|
||||
if (checkRegisteredModules(state, name) == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!push_module(state, name, bytecode))
|
||||
{
|
||||
return false;
|
||||
@@ -462,6 +602,12 @@ bool ScriptingVM::registerModule(lua_State* state,
|
||||
const char* name,
|
||||
Span<uint8_t> bytecode)
|
||||
{
|
||||
// Check if already registered
|
||||
if (checkRegisteredModules(state, name) == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
lua_pushcfunction(state, register_module, nullptr);
|
||||
lua_pushstring(state, name);
|
||||
if (!push_module(state, name, bytecode))
|
||||
@@ -498,6 +644,22 @@ bool ScriptingVM::registerScript(const char* name, Span<uint8_t> bytecode)
|
||||
return registerScript(m_state, name, bytecode);
|
||||
}
|
||||
|
||||
void ScriptingContext::queuePendingModule(ModuleDetails* moduleDetails)
|
||||
{
|
||||
m_pendingModules.push_back(moduleDetails);
|
||||
}
|
||||
|
||||
void ScriptingContext::clearPendingModule(const std::string& name)
|
||||
{
|
||||
m_pendingModules.erase(std::remove_if(m_pendingModules.begin(),
|
||||
m_pendingModules.end(),
|
||||
[&name](ModuleDetails* module) {
|
||||
return module->moduleName() ==
|
||||
name;
|
||||
}),
|
||||
m_pendingModules.end());
|
||||
}
|
||||
|
||||
int CPPRuntimeScriptingContext::pCall(lua_State* state, int nargs, int nresults)
|
||||
{
|
||||
// calculate stack position for message handler
|
||||
|
||||
@@ -3,9 +3,20 @@
|
||||
#include "rive/importers/scripted_object_importer.hpp"
|
||||
#include "rive/scripted/scripted_drawable.hpp"
|
||||
#include "rive/script_input_artboard.hpp"
|
||||
#include "rive/custom_property_container.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
ScriptInputArtboard::~ScriptInputArtboard()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->removeProperty(this);
|
||||
}
|
||||
m_artboard = nullptr;
|
||||
}
|
||||
|
||||
StatusCode ScriptInputArtboard::import(ImportStack& importStack)
|
||||
{
|
||||
auto backboardImporter =
|
||||
@@ -34,6 +45,27 @@ StatusCode ScriptInputArtboard::import(ImportStack& importStack)
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
StatusCode ScriptInputArtboard::onAddedClean(CoreContext* context)
|
||||
{
|
||||
StatusCode code = Super::onAddedClean(context);
|
||||
if (code != StatusCode::Ok)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
auto p = parent();
|
||||
if (p != nullptr)
|
||||
{
|
||||
auto scriptedObj = ScriptedObject::from(p);
|
||||
if (scriptedObj != nullptr)
|
||||
{
|
||||
scriptedObj->addProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
Core* ScriptInputArtboard::clone() const
|
||||
{
|
||||
ScriptInputArtboard* twin =
|
||||
|
||||
@@ -3,9 +3,19 @@
|
||||
#include "rive/importers/scripted_object_importer.hpp"
|
||||
#include "rive/scripted/scripted_drawable.hpp"
|
||||
#include "rive/script_input_boolean.hpp"
|
||||
#include "rive/custom_property_container.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
ScriptInputBoolean::~ScriptInputBoolean()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->removeProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
StatusCode ScriptInputBoolean::import(ImportStack& importStack)
|
||||
{
|
||||
auto importer =
|
||||
@@ -24,4 +34,34 @@ StatusCode ScriptInputBoolean::import(ImportStack& importStack)
|
||||
return Super::import(importStack);
|
||||
}
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
StatusCode ScriptInputBoolean::onAddedClean(CoreContext* context)
|
||||
{
|
||||
StatusCode code = Super::onAddedClean(context);
|
||||
if (code != StatusCode::Ok)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
auto p = parent();
|
||||
if (p != nullptr)
|
||||
{
|
||||
auto scriptedObj = ScriptedObject::from(p);
|
||||
if (scriptedObj != nullptr)
|
||||
{
|
||||
scriptedObj->addProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
void ScriptInputBoolean::propertyValueChanged()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->setBooleanInput(name(), propertyValue());
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,19 @@
|
||||
#include "rive/importers/scripted_object_importer.hpp"
|
||||
#include "rive/scripted/scripted_drawable.hpp"
|
||||
#include "rive/script_input_color.hpp"
|
||||
#include "rive/custom_property_container.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
ScriptInputColor::~ScriptInputColor()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->removeProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
StatusCode ScriptInputColor::import(ImportStack& importStack)
|
||||
{
|
||||
auto importer =
|
||||
@@ -24,4 +34,34 @@ StatusCode ScriptInputColor::import(ImportStack& importStack)
|
||||
return Super::import(importStack);
|
||||
}
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
StatusCode ScriptInputColor::onAddedClean(CoreContext* context)
|
||||
{
|
||||
StatusCode code = Super::onAddedClean(context);
|
||||
if (code != StatusCode::Ok)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
auto p = parent();
|
||||
if (p != nullptr)
|
||||
{
|
||||
auto scriptedObj = ScriptedObject::from(p);
|
||||
if (scriptedObj != nullptr)
|
||||
{
|
||||
scriptedObj->addProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
void ScriptInputColor::propertyValueChanged()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->setIntegerInput(name(), propertyValue());
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,19 @@
|
||||
#include "rive/importers/scripted_object_importer.hpp"
|
||||
#include "rive/scripted/scripted_drawable.hpp"
|
||||
#include "rive/script_input_number.hpp"
|
||||
#include "rive/custom_property_container.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
ScriptInputNumber::~ScriptInputNumber()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->removeProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
StatusCode ScriptInputNumber::import(ImportStack& importStack)
|
||||
{
|
||||
auto importer =
|
||||
@@ -24,4 +34,34 @@ StatusCode ScriptInputNumber::import(ImportStack& importStack)
|
||||
return Super::import(importStack);
|
||||
}
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
StatusCode ScriptInputNumber::onAddedClean(CoreContext* context)
|
||||
{
|
||||
StatusCode code = Super::onAddedClean(context);
|
||||
if (code != StatusCode::Ok)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
auto p = parent();
|
||||
if (p != nullptr)
|
||||
{
|
||||
auto scriptedObj = ScriptedObject::from(p);
|
||||
if (scriptedObj != nullptr)
|
||||
{
|
||||
scriptedObj->addProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
void ScriptInputNumber::propertyValueChanged()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->setNumberInput(name(), propertyValue());
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,19 @@
|
||||
#include "rive/importers/scripted_object_importer.hpp"
|
||||
#include "rive/scripted/scripted_drawable.hpp"
|
||||
#include "rive/script_input_string.hpp"
|
||||
#include "rive/custom_property_container.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
ScriptInputString::~ScriptInputString()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->removeProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
StatusCode ScriptInputString::import(ImportStack& importStack)
|
||||
{
|
||||
auto importer =
|
||||
@@ -24,4 +34,34 @@ StatusCode ScriptInputString::import(ImportStack& importStack)
|
||||
return Super::import(importStack);
|
||||
}
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
StatusCode ScriptInputString::onAddedClean(CoreContext* context)
|
||||
{
|
||||
StatusCode code = Super::onAddedClean(context);
|
||||
if (code != StatusCode::Ok)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
auto p = parent();
|
||||
if (p != nullptr)
|
||||
{
|
||||
auto scriptedObj = ScriptedObject::from(p);
|
||||
if (scriptedObj != nullptr)
|
||||
{
|
||||
scriptedObj->addProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
void ScriptInputString::propertyValueChanged()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->setStringInput(name(), propertyValue());
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,19 @@
|
||||
#include "rive/importers/scripted_object_importer.hpp"
|
||||
#include "rive/scripted/scripted_drawable.hpp"
|
||||
#include "rive/script_input_trigger.hpp"
|
||||
#include "rive/custom_property_container.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
ScriptInputTrigger::~ScriptInputTrigger()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->removeProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
StatusCode ScriptInputTrigger::import(ImportStack& importStack)
|
||||
{
|
||||
auto importer =
|
||||
@@ -24,4 +34,33 @@ StatusCode ScriptInputTrigger::import(ImportStack& importStack)
|
||||
return Super::import(importStack);
|
||||
}
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
StatusCode ScriptInputTrigger::onAddedClean(CoreContext* context)
|
||||
{
|
||||
StatusCode code = Super::onAddedClean(context);
|
||||
if (code != StatusCode::Ok)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
auto p = parent();
|
||||
if (p != nullptr)
|
||||
{
|
||||
auto scriptedObj = ScriptedObject::from(p);
|
||||
if (scriptedObj != nullptr)
|
||||
{
|
||||
scriptedObj->addProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
void ScriptInputTrigger::propertyValueChanged()
|
||||
{
|
||||
if (propertyValue() != 0)
|
||||
{
|
||||
scriptedObject()->trigger(name());
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,19 @@
|
||||
#include "rive/viewmodel/viewmodel_instance_value.hpp"
|
||||
#include "rive/viewmodel/viewmodel_property_enum_custom.hpp"
|
||||
#include "rive/viewmodel/viewmodel_property_viewmodel.hpp"
|
||||
#include "rive/custom_property_container.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
ScriptInputViewModelProperty::~ScriptInputViewModelProperty()
|
||||
{
|
||||
auto obj = scriptedObject();
|
||||
if (obj != nullptr)
|
||||
{
|
||||
obj->removeProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptInputViewModelProperty::decodeDataBindPathIds(
|
||||
Span<const uint8_t> value)
|
||||
{
|
||||
@@ -83,5 +93,26 @@ StatusCode ScriptInputViewModelProperty::import(ImportStack& importStack)
|
||||
// to add it as a Component, otherwise, return Ok
|
||||
return Super::import(importStack);
|
||||
}
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
|
||||
StatusCode ScriptInputViewModelProperty::onAddedClean(CoreContext* context)
|
||||
{
|
||||
StatusCode code = Super::onAddedClean(context);
|
||||
if (code != StatusCode::Ok)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
auto p = parent();
|
||||
if (p != nullptr)
|
||||
{
|
||||
auto scriptedObj = ScriptedObject::from(p);
|
||||
if (scriptedObj != nullptr)
|
||||
{
|
||||
scriptedObj->addProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode::Ok;
|
||||
}
|
||||
@@ -17,12 +17,29 @@ 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
|
||||
bool ScriptedDataConverter::scriptInit(LuaState* state)
|
||||
{
|
||||
@@ -158,6 +175,16 @@ bool ScriptedDataConverter::advanceComponent(float elapsedSeconds,
|
||||
return scriptAdvance(elapsedSeconds);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -180,19 +207,6 @@ Core* ScriptedDataConverter::clone() const
|
||||
{
|
||||
auto clonedValue = prop->clone()->as<CustomProperty>();
|
||||
twin->addProperty(clonedValue);
|
||||
auto scriptInput = ScriptInput::from(clonedValue);
|
||||
if (scriptInput != nullptr)
|
||||
{
|
||||
scriptInput->scriptedObject(twin);
|
||||
if (scriptInput->dataBind() != nullptr)
|
||||
{
|
||||
auto clonedDataBind =
|
||||
scriptInput->dataBind()->clone()->as<DataBind>();
|
||||
clonedDataBind->target(clonedValue);
|
||||
scriptInput->dataBind(clonedDataBind);
|
||||
twin->addDirtyDataBind(clonedDataBind);
|
||||
}
|
||||
}
|
||||
}
|
||||
return twin;
|
||||
}
|
||||
@@ -171,6 +171,16 @@ bool ScriptedDrawable::addScriptedDirt(ComponentDirt value, bool recurse)
|
||||
return Drawable::addDirt(value, recurse);
|
||||
}
|
||||
|
||||
void ScriptedDrawable::addProperty(CustomProperty* prop)
|
||||
{
|
||||
auto scriptInput = ScriptInput::from(prop);
|
||||
if (scriptInput != nullptr)
|
||||
{
|
||||
scriptInput->scriptedObject(this);
|
||||
}
|
||||
CustomPropertyContainer::addProperty(prop);
|
||||
}
|
||||
|
||||
StatusCode ScriptedDrawable::import(ImportStack& importStack)
|
||||
{
|
||||
auto result = registerReferencer(importStack);
|
||||
@@ -189,16 +199,6 @@ Core* ScriptedDrawable::clone() const
|
||||
{
|
||||
twin->setAsset(m_fileAsset);
|
||||
}
|
||||
for (auto prop : m_customProperties)
|
||||
{
|
||||
auto clonedValue = prop->clone()->as<CustomProperty>();
|
||||
twin->addProperty(clonedValue);
|
||||
auto scriptInput = ScriptInput::from(clonedValue);
|
||||
if (scriptInput != nullptr)
|
||||
{
|
||||
scriptInput->scriptedObject(twin);
|
||||
}
|
||||
}
|
||||
return twin;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +129,16 @@ void ScriptedLayout::controlSize(Vec2D size,
|
||||
}
|
||||
#endif
|
||||
|
||||
void ScriptedLayout::addProperty(CustomProperty* prop)
|
||||
{
|
||||
auto scriptInput = ScriptInput::from(prop);
|
||||
if (scriptInput != nullptr)
|
||||
{
|
||||
scriptInput->scriptedObject(this);
|
||||
}
|
||||
CustomPropertyContainer::addProperty(prop);
|
||||
}
|
||||
|
||||
Core* ScriptedLayout::clone() const
|
||||
{
|
||||
ScriptedLayout* twin = ScriptedLayoutBase::clone()->as<ScriptedLayout>();
|
||||
@@ -136,15 +146,5 @@ Core* ScriptedLayout::clone() const
|
||||
{
|
||||
twin->setAsset(m_fileAsset);
|
||||
}
|
||||
for (auto prop : m_customProperties)
|
||||
{
|
||||
auto clonedValue = prop->clone()->as<CustomProperty>();
|
||||
twin->addProperty(clonedValue);
|
||||
auto scriptInput = ScriptInput::from(clonedValue);
|
||||
if (scriptInput != nullptr)
|
||||
{
|
||||
scriptInput->scriptedObject(twin);
|
||||
}
|
||||
}
|
||||
return twin;
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "rive/assets/script_asset.hpp"
|
||||
#include "rive/artboard.hpp"
|
||||
#include "rive/file.hpp"
|
||||
#include "rive/script_input_artboard.hpp"
|
||||
#include "rive/scripted/scripted_data_converter.hpp"
|
||||
#include "rive/scripted/scripted_drawable.hpp"
|
||||
#include "rive/scripted/scripted_layout.hpp"
|
||||
@@ -35,6 +36,7 @@ void ScriptedObject::setArtboardInput(std::string name, Artboard* artboard)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto state = m_state->state;
|
||||
rive_lua_pushRef(state, m_self);
|
||||
lua_newrive<ScriptedArtboard>(state,
|
||||
@@ -298,12 +300,32 @@ bool ScriptedObject::scriptInit(LuaState* luaState)
|
||||
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::scriptDispose()
|
||||
{
|
||||
disposeScriptInputs();
|
||||
|
||||
if (m_state != nullptr)
|
||||
{
|
||||
lua_unref(m_state->state, m_self);
|
||||
lua_unref(m_state->state, m_context);
|
||||
#ifdef TESTING
|
||||
// Force GC to collect any ScriptedArtboard instances created via
|
||||
// instance()
|
||||
lua_gc(m_state->state, LUA_GCCOLLECT, 0);
|
||||
#endif
|
||||
}
|
||||
m_state = nullptr;
|
||||
m_self = 0;
|
||||
@@ -330,6 +352,8 @@ bool ScriptedObject::scriptAdvance(float elapsedSeconds) { return false; }
|
||||
void ScriptedObject::scriptUpdate() {}
|
||||
|
||||
void ScriptedObject::scriptDispose() {}
|
||||
|
||||
void ScriptedObject::disposeScriptInputs() {}
|
||||
#endif
|
||||
|
||||
void ScriptedObject::reinit()
|
||||
|
||||
@@ -107,6 +107,16 @@ bool ScriptedPathEffect::addScriptedDirt(ComponentDirt value, bool recurse)
|
||||
return Component::addDirt(value, recurse);
|
||||
}
|
||||
|
||||
void ScriptedPathEffect::addProperty(CustomProperty* prop)
|
||||
{
|
||||
auto scriptInput = ScriptInput::from(prop);
|
||||
if (scriptInput != nullptr)
|
||||
{
|
||||
scriptInput->scriptedObject(this);
|
||||
}
|
||||
CustomPropertyContainer::addProperty(prop);
|
||||
}
|
||||
|
||||
StatusCode ScriptedPathEffect::import(ImportStack& importStack)
|
||||
{
|
||||
auto result = registerReferencer(importStack);
|
||||
@@ -127,16 +137,6 @@ Core* ScriptedPathEffect::clone() const
|
||||
{
|
||||
twin->setAsset(m_fileAsset);
|
||||
}
|
||||
for (auto prop : m_customProperties)
|
||||
{
|
||||
auto clonedValue = prop->clone()->as<CustomProperty>();
|
||||
twin->addProperty(clonedValue);
|
||||
auto scriptInput = ScriptInput::from(clonedValue);
|
||||
if (scriptInput != nullptr)
|
||||
{
|
||||
scriptInput->scriptedObject(twin);
|
||||
}
|
||||
}
|
||||
return twin;
|
||||
}
|
||||
|
||||
|
||||
BIN
tests/unit_tests/assets/script_artboard_test.riv
Normal file
BIN
tests/unit_tests/assets/script_artboard_test.riv
Normal file
Binary file not shown.
BIN
tests/unit_tests/assets/script_dependency_test.riv
Normal file
BIN
tests/unit_tests/assets/script_dependency_test.riv
Normal file
Binary file not shown.
BIN
tests/unit_tests/assets/script_inputs_test_1.riv
Normal file
BIN
tests/unit_tests/assets/script_inputs_test_1.riv
Normal file
Binary file not shown.
BIN
tests/unit_tests/assets/script_layout_test.riv
Normal file
BIN
tests/unit_tests/assets/script_layout_test.riv
Normal file
Binary file not shown.
BIN
tests/unit_tests/assets/script_path_effects_test.riv
Normal file
BIN
tests/unit_tests/assets/script_path_effects_test.riv
Normal file
Binary file not shown.
BIN
tests/unit_tests/assets/script_paths_test.riv
Normal file
BIN
tests/unit_tests/assets/script_paths_test.riv
Normal file
Binary file not shown.
BIN
tests/unit_tests/assets/script_string_converter_test.riv
Normal file
BIN
tests/unit_tests/assets/script_string_converter_test.riv
Normal file
Binary file not shown.
@@ -1,8 +1,11 @@
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "scripting_test_utilities.hpp"
|
||||
#include "rive/animation/state_machine_instance.hpp"
|
||||
#include "rive/lua/rive_lua_libs.hpp"
|
||||
#include "rive/math/path_types.hpp"
|
||||
#include "rive_file_reader.hpp"
|
||||
#include "rive_testing.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
@@ -367,3 +370,57 @@ TEST_CASE("path measure extract across multiple contours", "[scripting]")
|
||||
// Should extract from both contours
|
||||
CHECK(destPath->rawPath.verbs().count() > 0);
|
||||
}
|
||||
|
||||
TEST_CASE("path drawing examples", "[silver]")
|
||||
{
|
||||
rive::SerializingFactory silver;
|
||||
auto file = ReadRiveFile("assets/script_paths_test.riv", &silver);
|
||||
|
||||
auto artboard = file->artboardNamed("PathsScript");
|
||||
|
||||
silver.frameSize(artboard->width(), artboard->height());
|
||||
|
||||
REQUIRE(artboard != nullptr);
|
||||
auto stateMachine = artboard->stateMachineAt(0);
|
||||
stateMachine->advanceAndApply(0.1f);
|
||||
|
||||
auto renderer = silver.makeRenderer();
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
int frames = 60;
|
||||
for (int i = 0; i < frames; i++)
|
||||
{
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
}
|
||||
|
||||
CHECK(silver.matches("script_paths"));
|
||||
}
|
||||
|
||||
TEST_CASE("path effects examples", "[silver]")
|
||||
{
|
||||
rive::SerializingFactory silver;
|
||||
auto file = ReadRiveFile("assets/script_path_effects_test.riv", &silver);
|
||||
|
||||
auto artboard = file->artboardNamed("PathEffects");
|
||||
|
||||
silver.frameSize(artboard->width(), artboard->height());
|
||||
|
||||
REQUIRE(artboard != nullptr);
|
||||
auto stateMachine = artboard->stateMachineAt(0);
|
||||
stateMachine->advanceAndApply(0.1f);
|
||||
|
||||
auto renderer = silver.makeRenderer();
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
int frames = 60;
|
||||
for (int i = 0; i < frames; i++)
|
||||
{
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
}
|
||||
|
||||
CHECK(silver.matches("script_path_effects"));
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "scripting_test_utilities.hpp"
|
||||
#include "rive/animation/state_machine_instance.hpp"
|
||||
#include "rive/lua/rive_lua_libs.hpp"
|
||||
#include "rive_file_reader.hpp"
|
||||
|
||||
@@ -282,4 +283,31 @@ TEST_CASE("can add artboard to path", "[scripting]")
|
||||
lua_getglobal(L, "addToPath");
|
||||
lua_pushvalue(L, -2);
|
||||
CHECK(lua_pcall(L, 1, 1, 0) == LUA_OK);
|
||||
}
|
||||
|
||||
TEST_CASE("script instances Artboard input", "[silver]")
|
||||
{
|
||||
rive::SerializingFactory silver;
|
||||
auto file = ReadRiveFile("assets/script_artboard_test.riv", &silver);
|
||||
|
||||
auto artboard = file->artboardNamed("Artboard");
|
||||
|
||||
silver.frameSize(artboard->width(), artboard->height());
|
||||
|
||||
REQUIRE(artboard != nullptr);
|
||||
auto stateMachine = artboard->stateMachineAt(0);
|
||||
stateMachine->advanceAndApply(0.1f);
|
||||
|
||||
auto renderer = silver.makeRenderer();
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
int frames = 60;
|
||||
for (int i = 0; i < frames; i++)
|
||||
{
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
}
|
||||
|
||||
CHECK(silver.matches("script_artboards"));
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "scripting_test_utilities.hpp"
|
||||
#include "rive/animation/state_machine_instance.hpp"
|
||||
#include "rive/lua/rive_lua_libs.hpp"
|
||||
#include "rive/viewmodel/viewmodel_instance_number.hpp"
|
||||
#include "rive/viewmodel/viewmodel_instance_string.hpp"
|
||||
#include "rive_file_reader.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
@@ -342,3 +346,101 @@ end
|
||||
CHECK(top == lua_gettop(L));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("scripted string converter", "[silver]")
|
||||
{
|
||||
rive::SerializingFactory silver;
|
||||
auto file =
|
||||
ReadRiveFile("assets/script_string_converter_test.riv", &silver);
|
||||
auto artboard = file->artboardNamed("Converter");
|
||||
|
||||
silver.frameSize(artboard->width(), artboard->height());
|
||||
REQUIRE(artboard != nullptr);
|
||||
auto stateMachine = artboard->stateMachineAt(0);
|
||||
int viewModelId = artboard.get()->viewModelId();
|
||||
|
||||
auto vmi = viewModelId == -1
|
||||
? file->createViewModelInstance(artboard.get())
|
||||
: file->createViewModelInstance(viewModelId, 0);
|
||||
stateMachine->bindViewModelInstance(vmi);
|
||||
stateMachine->advanceAndApply(0.1f);
|
||||
|
||||
auto renderer = silver.makeRenderer();
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
auto field1 =
|
||||
vmi->propertyValue("Field1")->as<rive::ViewModelInstanceString>();
|
||||
REQUIRE(field1 != nullptr);
|
||||
field1->propertyValue("H#e%l&l*o");
|
||||
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
auto field2 =
|
||||
vmi->propertyValue("Field2")->as<rive::ViewModelInstanceString>();
|
||||
REQUIRE(field2 != nullptr);
|
||||
field2->propertyValue("____one two three___");
|
||||
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
auto field3 =
|
||||
vmi->propertyValue("Field3")->as<rive::ViewModelInstanceString>();
|
||||
REQUIRE(field3 != nullptr);
|
||||
field3->propertyValue(" **This uses a string converter@@. ");
|
||||
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
auto field4 =
|
||||
vmi->propertyValue("Field4")->as<rive::ViewModelInstanceString>();
|
||||
REQUIRE(field4 != nullptr);
|
||||
field4->propertyValue("It strips special characters like *&^%$#@!)()");
|
||||
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
CHECK(silver.matches("script_string_converter"));
|
||||
}
|
||||
|
||||
TEST_CASE("scripted data converter using multi chain requires", "[silver]")
|
||||
{
|
||||
rive::SerializingFactory silver;
|
||||
auto file = ReadRiveFile("assets/script_dependency_test.riv", &silver);
|
||||
auto artboard = file->artboardNamed("Artboard");
|
||||
|
||||
silver.frameSize(artboard->width(), artboard->height());
|
||||
REQUIRE(artboard != nullptr);
|
||||
auto stateMachine = artboard->stateMachineAt(0);
|
||||
int viewModelId = artboard.get()->viewModelId();
|
||||
|
||||
auto vmi = viewModelId == -1
|
||||
? file->createViewModelInstance(artboard.get())
|
||||
: file->createViewModelInstance(viewModelId, 0);
|
||||
stateMachine->bindViewModelInstance(vmi);
|
||||
stateMachine->advanceAndApply(0.1f);
|
||||
|
||||
auto renderer = silver.makeRenderer();
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
rive::ViewModelInstanceNumber* num =
|
||||
vmi->propertyValue("InputValue1")->as<rive::ViewModelInstanceNumber>();
|
||||
REQUIRE(num != nullptr);
|
||||
|
||||
int counter = 0;
|
||||
int frames = 30;
|
||||
for (int i = 0; i < frames; i++)
|
||||
{
|
||||
num->propertyValue(counter);
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
counter += 5;
|
||||
}
|
||||
|
||||
CHECK(silver.matches("script_converter_with_dependency"));
|
||||
}
|
||||
@@ -8,6 +8,9 @@
|
||||
#include "utils/no_op_renderer.hpp"
|
||||
#include "rive/layout/layout_enums.hpp"
|
||||
#include "rive/data_bind/data_values/data_value.hpp"
|
||||
#include "rive/viewmodel/viewmodel_instance_color.hpp"
|
||||
#include "rive/viewmodel/viewmodel_instance_trigger.hpp"
|
||||
#include "rive_file_reader.hpp"
|
||||
#include <memory>
|
||||
|
||||
using namespace rive;
|
||||
@@ -758,3 +761,39 @@ end
|
||||
// Cleanup - must happen before luaState goes out of scope
|
||||
converter->scriptDispose();
|
||||
}
|
||||
|
||||
TEST_CASE("scripted input color and trigger test", "[silver]")
|
||||
{
|
||||
rive::SerializingFactory silver;
|
||||
auto file = ReadRiveFile("assets/script_inputs_test_1.riv", &silver);
|
||||
auto artboard = file->artboardNamed("Artboard");
|
||||
|
||||
silver.frameSize(artboard->width(), artboard->height());
|
||||
REQUIRE(artboard != nullptr);
|
||||
auto stateMachine = artboard->stateMachineAt(0);
|
||||
int viewModelId = artboard.get()->viewModelId();
|
||||
|
||||
auto vmi = viewModelId == -1
|
||||
? file->createViewModelInstance(artboard.get())
|
||||
: file->createViewModelInstance(viewModelId, 0);
|
||||
stateMachine->bindViewModelInstance(vmi);
|
||||
stateMachine->advanceAndApply(0.1f);
|
||||
|
||||
auto renderer = silver.makeRenderer();
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
rive::ViewModelInstanceTrigger* trigger =
|
||||
vmi->propertyValue("Trigger")->as<rive::ViewModelInstanceTrigger>();
|
||||
REQUIRE(trigger != nullptr);
|
||||
|
||||
int frames = 30;
|
||||
for (int i = 0; i < frames; i++)
|
||||
{
|
||||
trigger->trigger();
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
}
|
||||
|
||||
CHECK(silver.matches("script_input_color_trigger"));
|
||||
}
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "scripting_test_utilities.hpp"
|
||||
#include "rive/animation/state_machine_instance.hpp"
|
||||
#include "rive/lua/rive_lua_libs.hpp"
|
||||
#include "rive/scripted/scripted_layout.hpp"
|
||||
#include "rive/layout/layout_enums.hpp"
|
||||
#include "rive/math/vec2d.hpp"
|
||||
#include "rive/viewmodel/viewmodel_instance_number.hpp"
|
||||
#include "rive_file_reader.hpp"
|
||||
#include "utils/no_op_renderer.hpp"
|
||||
|
||||
using namespace rive;
|
||||
@@ -287,3 +290,55 @@ end
|
||||
CHECK(top == lua_gettop(L));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("layout grid script", "[silver]")
|
||||
{
|
||||
rive::SerializingFactory silver;
|
||||
auto file = ReadRiveFile("assets/script_layout_test.riv", &silver);
|
||||
auto artboard = file->artboardNamed("LayoutScript");
|
||||
|
||||
silver.frameSize(artboard->width(), artboard->height());
|
||||
REQUIRE(artboard != nullptr);
|
||||
auto stateMachine = artboard->stateMachineAt(0);
|
||||
int viewModelId = artboard.get()->viewModelId();
|
||||
|
||||
auto vmi = viewModelId == -1
|
||||
? file->createViewModelInstance(artboard.get())
|
||||
: file->createViewModelInstance(viewModelId, 0);
|
||||
stateMachine->bindViewModelInstance(vmi);
|
||||
auto rows = vmi->propertyValue("Rows")->as<rive::ViewModelInstanceNumber>();
|
||||
REQUIRE(rows != nullptr);
|
||||
auto rowsValue = rows->propertyValue();
|
||||
REQUIRE(rowsValue == 5);
|
||||
auto cols =
|
||||
vmi->propertyValue("Columns")->as<rive::ViewModelInstanceNumber>();
|
||||
REQUIRE(cols != nullptr);
|
||||
auto colsValue = cols->propertyValue();
|
||||
REQUIRE(colsValue == 5);
|
||||
stateMachine->advanceAndApply(0.1f);
|
||||
|
||||
auto renderer = silver.makeRenderer();
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
int frames = 20;
|
||||
for (int i = 0; i < frames; i++)
|
||||
{
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
}
|
||||
|
||||
rows->propertyValue(8);
|
||||
rowsValue = rows->propertyValue();
|
||||
REQUIRE(rowsValue == 8);
|
||||
cols->propertyValue(7);
|
||||
colsValue = cols->propertyValue();
|
||||
REQUIRE(colsValue == 7);
|
||||
for (int i = 0; i < frames; i++)
|
||||
{
|
||||
silver.addFrame();
|
||||
stateMachine->advanceAndApply(0.016f);
|
||||
artboard->draw(renderer.get());
|
||||
}
|
||||
CHECK(silver.matches("script_layout_grid"));
|
||||
}
|
||||
BIN
tests/unit_tests/silvers/script_artboards.sriv
Normal file
BIN
tests/unit_tests/silvers/script_artboards.sriv
Normal file
Binary file not shown.
BIN
tests/unit_tests/silvers/script_converter_with_dependency.sriv
Normal file
BIN
tests/unit_tests/silvers/script_converter_with_dependency.sriv
Normal file
Binary file not shown.
BIN
tests/unit_tests/silvers/script_input_color_trigger.sriv
Normal file
BIN
tests/unit_tests/silvers/script_input_color_trigger.sriv
Normal file
Binary file not shown.
BIN
tests/unit_tests/silvers/script_layout_grid.sriv
Normal file
BIN
tests/unit_tests/silvers/script_layout_grid.sriv
Normal file
Binary file not shown.
BIN
tests/unit_tests/silvers/script_path_effects.sriv
Normal file
BIN
tests/unit_tests/silvers/script_path_effects.sriv
Normal file
Binary file not shown.
BIN
tests/unit_tests/silvers/script_paths.sriv
Normal file
BIN
tests/unit_tests/silvers/script_paths.sriv
Normal file
Binary file not shown.
BIN
tests/unit_tests/silvers/script_string_converter.sriv
Normal file
BIN
tests/unit_tests/silvers/script_string_converter.sriv
Normal file
Binary file not shown.
Reference in New Issue
Block a user