mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
Support for Triggers in Custom property groups (#10322) 9af6af0361
Adds support for Triggers in Custom Property Groups. This will allow triggers to be keyframed, which can also be bound to ViewModel triggers. Currently an event has to be fired on the timeline in order to fire a ViewModel trigger via a listener. Co-authored-by: Philip Chung <philterdesign@gmail.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
4bd8c63b93672af82819728be047578d71018049
|
||||
9af6af0361de2258ff05a708cacc8e58899e0d4a
|
||||
|
||||
30
dev/defs/custom_property_trigger.json
Normal file
30
dev/defs/custom_property_trigger.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "CustomPropertyTrigger",
|
||||
"key": {
|
||||
"int": 613,
|
||||
"string": "custompropertytrigger"
|
||||
},
|
||||
"extends": "custom_property.json",
|
||||
"properties": {
|
||||
"fire": {
|
||||
"type": "callback",
|
||||
"animates": true,
|
||||
"key": {
|
||||
"int": 869,
|
||||
"string": "fire"
|
||||
},
|
||||
"description": "Callback type used for keying the trigger"
|
||||
},
|
||||
"propertyValue": {
|
||||
"type": "uint",
|
||||
"initialValue": "0",
|
||||
"animates": true,
|
||||
"key": {
|
||||
"int": 870,
|
||||
"string": "propertyvalue"
|
||||
},
|
||||
"description": "Property value used to bind the trigger",
|
||||
"bindable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,6 +76,7 @@ private:
|
||||
std::vector<ArtboardComponentList*> m_ComponentLists;
|
||||
std::vector<ArtboardHost*> m_ArtboardHosts;
|
||||
std::vector<Joystick*> m_Joysticks;
|
||||
std::vector<ResettingComponent*> m_Resettables;
|
||||
std::vector<DataBind*> m_DataBinds;
|
||||
std::vector<DataBind*> m_AllDataBinds;
|
||||
DataContext* m_DataContext = nullptr;
|
||||
|
||||
21
include/rive/custom_property_trigger.hpp
Normal file
21
include/rive/custom_property_trigger.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef _RIVE_CUSTOM_PROPERTY_TRIGGER_HPP_
|
||||
#define _RIVE_CUSTOM_PROPERTY_TRIGGER_HPP_
|
||||
#include "rive/generated/custom_property_trigger_base.hpp"
|
||||
#include "rive/resetting_component.hpp"
|
||||
#include <stdio.h>
|
||||
namespace rive
|
||||
{
|
||||
class CustomPropertyTrigger : public CustomPropertyTriggerBase,
|
||||
public ResettingComponent
|
||||
{
|
||||
public:
|
||||
void fire(const CallbackData& value) override
|
||||
{
|
||||
propertyValue(propertyValue() + 1);
|
||||
}
|
||||
|
||||
void reset() override { propertyValue(0); }
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -129,6 +129,7 @@
|
||||
#include "rive/custom_property_group.hpp"
|
||||
#include "rive/custom_property_number.hpp"
|
||||
#include "rive/custom_property_string.hpp"
|
||||
#include "rive/custom_property_trigger.hpp"
|
||||
#include "rive/data_bind/bindable_property.hpp"
|
||||
#include "rive/data_bind/bindable_property_artboard.hpp"
|
||||
#include "rive/data_bind/bindable_property_asset.hpp"
|
||||
@@ -748,6 +749,8 @@ public:
|
||||
return new FileAssetContents();
|
||||
case AudioEventBase::typeKey:
|
||||
return new AudioEvent();
|
||||
case CustomPropertyTriggerBase::typeKey:
|
||||
return new CustomPropertyTrigger();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1425,6 +1428,9 @@ public:
|
||||
case AudioEventBase::assetIdPropertyKey:
|
||||
object->as<AudioEventBase>()->assetId(value);
|
||||
break;
|
||||
case CustomPropertyTriggerBase::propertyValuePropertyKey:
|
||||
object->as<CustomPropertyTriggerBase>()->propertyValue(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
static void setString(Core* object, int propertyKey, std::string value)
|
||||
@@ -2318,6 +2324,9 @@ public:
|
||||
case EventBase::triggerPropertyKey:
|
||||
object->as<EventBase>()->trigger(value);
|
||||
break;
|
||||
case CustomPropertyTriggerBase::firePropertyKey:
|
||||
object->as<CustomPropertyTriggerBase>()->fire(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
static uint32_t getUint(Core* object, int propertyKey)
|
||||
@@ -2804,6 +2813,8 @@ public:
|
||||
return object->as<FileAssetBase>()->assetId();
|
||||
case AudioEventBase::assetIdPropertyKey:
|
||||
return object->as<AudioEventBase>()->assetId();
|
||||
case CustomPropertyTriggerBase::propertyValuePropertyKey:
|
||||
return object->as<CustomPropertyTriggerBase>()->propertyValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -3636,6 +3647,7 @@ public:
|
||||
case TextValueRunBase::styleIdPropertyKey:
|
||||
case FileAssetBase::assetIdPropertyKey:
|
||||
case AudioEventBase::assetIdPropertyKey:
|
||||
case CustomPropertyTriggerBase::propertyValuePropertyKey:
|
||||
return CoreUintType::id;
|
||||
case ViewModelComponentBase::namePropertyKey:
|
||||
case DataEnumCustomBase::namePropertyKey:
|
||||
@@ -3942,6 +3954,7 @@ public:
|
||||
{
|
||||
case NestedTriggerBase::firePropertyKey:
|
||||
case EventBase::triggerPropertyKey:
|
||||
case CustomPropertyTriggerBase::firePropertyKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -4359,6 +4372,8 @@ public:
|
||||
return object->is<FileAssetBase>();
|
||||
case AudioEventBase::assetIdPropertyKey:
|
||||
return object->is<AudioEventBase>();
|
||||
case CustomPropertyTriggerBase::propertyValuePropertyKey:
|
||||
return object->is<CustomPropertyTriggerBase>();
|
||||
case ViewModelComponentBase::namePropertyKey:
|
||||
return object->is<ViewModelComponentBase>();
|
||||
case DataEnumCustomBase::namePropertyKey:
|
||||
@@ -4927,6 +4942,8 @@ public:
|
||||
return object->is<NestedTriggerBase>();
|
||||
case EventBase::triggerPropertyKey:
|
||||
return object->is<EventBase>();
|
||||
case CustomPropertyTriggerBase::firePropertyKey:
|
||||
return object->is<CustomPropertyTriggerBase>();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
76
include/rive/generated/custom_property_trigger_base.hpp
Normal file
76
include/rive/generated/custom_property_trigger_base.hpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef _RIVE_CUSTOM_PROPERTY_TRIGGER_BASE_HPP_
|
||||
#define _RIVE_CUSTOM_PROPERTY_TRIGGER_BASE_HPP_
|
||||
#include "rive/core/field_types/core_callback_type.hpp"
|
||||
#include "rive/core/field_types/core_uint_type.hpp"
|
||||
#include "rive/custom_property.hpp"
|
||||
namespace rive
|
||||
{
|
||||
class CustomPropertyTriggerBase : public CustomProperty
|
||||
{
|
||||
protected:
|
||||
typedef CustomProperty Super;
|
||||
|
||||
public:
|
||||
static const uint16_t typeKey = 613;
|
||||
|
||||
/// Helper to quickly determine if a core object extends another without
|
||||
/// RTTI at runtime.
|
||||
bool isTypeOf(uint16_t typeKey) const override
|
||||
{
|
||||
switch (typeKey)
|
||||
{
|
||||
case CustomPropertyTriggerBase::typeKey:
|
||||
case CustomPropertyBase::typeKey:
|
||||
case ComponentBase::typeKey:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t coreType() const override { return typeKey; }
|
||||
|
||||
static const uint16_t firePropertyKey = 869;
|
||||
static const uint16_t propertyValuePropertyKey = 870;
|
||||
|
||||
protected:
|
||||
uint32_t m_PropertyValue = 0;
|
||||
|
||||
public:
|
||||
virtual void fire(const CallbackData& value) = 0;
|
||||
|
||||
inline uint32_t propertyValue() const { return m_PropertyValue; }
|
||||
void propertyValue(uint32_t value)
|
||||
{
|
||||
if (m_PropertyValue == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_PropertyValue = value;
|
||||
propertyValueChanged();
|
||||
}
|
||||
|
||||
Core* clone() const override;
|
||||
void copy(const CustomPropertyTriggerBase& object)
|
||||
{
|
||||
m_PropertyValue = object.m_PropertyValue;
|
||||
CustomProperty::copy(object);
|
||||
}
|
||||
|
||||
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
|
||||
{
|
||||
switch (propertyKey)
|
||||
{
|
||||
case propertyValuePropertyKey:
|
||||
m_PropertyValue = CoreUintType::deserialize(reader);
|
||||
return true;
|
||||
}
|
||||
return CustomProperty::deserialize(propertyKey, reader);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void propertyValueChanged() {}
|
||||
};
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "rive/artboard_component_list.hpp"
|
||||
#include "rive/backboard.hpp"
|
||||
#include "rive/animation/linear_animation_instance.hpp"
|
||||
#include "rive/custom_property_trigger.hpp"
|
||||
#include "rive/dependency_sorter.hpp"
|
||||
#include "rive/data_bind/data_bind.hpp"
|
||||
#include "rive/data_bind/data_bind_context.hpp"
|
||||
@@ -223,6 +224,14 @@ StatusCode Artboard::initialize()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
if (object->is<Component>())
|
||||
{
|
||||
auto resettable = ResettingComponent::from(object->as<Component>());
|
||||
if (resettable)
|
||||
{
|
||||
m_Resettables.push_back(resettable);
|
||||
}
|
||||
}
|
||||
switch (object->coreType())
|
||||
{
|
||||
case DrawRulesBase::typeKey:
|
||||
@@ -995,13 +1004,9 @@ bool Artboard::advanceInternal(float elapsedSeconds, AdvanceFlags flags)
|
||||
|
||||
void Artboard::reset()
|
||||
{
|
||||
for (auto dep : m_DependencyOrder)
|
||||
for (auto obj : m_Resettables)
|
||||
{
|
||||
auto adv = ResettingComponent::from(dep);
|
||||
if (adv != nullptr)
|
||||
{
|
||||
adv->reset();
|
||||
}
|
||||
obj->reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
11
src/generated/custom_property_trigger_base.cpp
Normal file
11
src/generated/custom_property_trigger_base.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "rive/generated/custom_property_trigger_base.hpp"
|
||||
#include "rive/custom_property_trigger.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
Core* CustomPropertyTriggerBase::clone() const
|
||||
{
|
||||
auto cloned = new CustomPropertyTrigger();
|
||||
cloned->copy(*this);
|
||||
return cloned;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "rive/resetting_component.hpp"
|
||||
#include "rive/artboard.hpp"
|
||||
#include "rive/artboard_component_list.hpp"
|
||||
#include "rive/custom_property_trigger.hpp"
|
||||
#include "rive/nested_artboard.hpp"
|
||||
#include "rive/nested_artboard_layout.hpp"
|
||||
#include "rive/nested_artboard_leaf.hpp"
|
||||
@@ -18,6 +19,8 @@ ResettingComponent* ResettingComponent::from(Component* component)
|
||||
return component->as<NestedArtboard>();
|
||||
case ArtboardComponentListBase::typeKey:
|
||||
return component->as<ArtboardComponentList>();
|
||||
case CustomPropertyTriggerBase::typeKey:
|
||||
return component->as<CustomPropertyTrigger>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
BIN
tests/unit_tests/assets/custom_property_trigger.riv
Normal file
BIN
tests/unit_tests/assets/custom_property_trigger.riv
Normal file
Binary file not shown.
@@ -8,9 +8,10 @@
|
||||
#include <rive/shapes/paint/fill.hpp>
|
||||
#include <rive/shapes/paint/solid_color.hpp>
|
||||
#include <rive/text/text_value_run.hpp>
|
||||
#include <rive/custom_property_boolean.hpp>
|
||||
#include <rive/custom_property_number.hpp>
|
||||
#include <rive/custom_property_string.hpp>
|
||||
#include <rive/custom_property_boolean.hpp>
|
||||
#include <rive/custom_property_trigger.hpp>
|
||||
#include <rive/constraints/follow_path_constraint.hpp>
|
||||
#include <rive/viewmodel/viewmodel_instance_number.hpp>
|
||||
#include <rive/viewmodel/viewmodel_instance_color.hpp>
|
||||
@@ -1182,3 +1183,43 @@ TEST_CASE("Trigger based listeners", "[data binding]")
|
||||
|
||||
CHECK(silver.matches("trigger_based_listeners"));
|
||||
}
|
||||
|
||||
TEST_CASE("Custom Property Trigger Binding", "[data binding]")
|
||||
{
|
||||
rive::SerializingFactory silver;
|
||||
auto file = ReadRiveFile("assets/custom_property_trigger.riv", &silver);
|
||||
|
||||
auto artboard = file->artboard("Main")->instance();
|
||||
REQUIRE(artboard != nullptr);
|
||||
silver.frameSize(artboard->width(), artboard->height());
|
||||
|
||||
auto viewModelInstance =
|
||||
file->createDefaultViewModelInstance(artboard.get());
|
||||
REQUIRE(viewModelInstance != nullptr);
|
||||
auto machine = artboard->defaultStateMachine();
|
||||
machine->bindViewModelInstance(viewModelInstance);
|
||||
REQUIRE(machine != nullptr);
|
||||
// Advance state machine
|
||||
machine->advanceAndApply(0.0f);
|
||||
|
||||
auto circle = artboard->find<rive::Shape>("MainCircle");
|
||||
REQUIRE(circle != nullptr);
|
||||
REQUIRE(circle->scaleX() == 1.0f);
|
||||
REQUIRE(circle->scaleY() == 1.0f);
|
||||
|
||||
auto trig = artboard->find<rive::CustomPropertyTrigger>("Trig");
|
||||
REQUIRE(trig != nullptr);
|
||||
|
||||
auto renderer = silver.makeRenderer();
|
||||
artboard->draw(renderer.get());
|
||||
|
||||
int frames = (int)(1.0f / 0.16f);
|
||||
for (int i = 0; i < frames; i++)
|
||||
{
|
||||
silver.addFrame();
|
||||
machine->advanceAndApply(0.16f);
|
||||
artboard->draw(renderer.get());
|
||||
}
|
||||
|
||||
CHECK(silver.matches("custom_property_trigger_bind"));
|
||||
}
|
||||
BIN
tests/unit_tests/silvers/custom_property_trigger_bind.sriv
Normal file
BIN
tests/unit_tests/silvers/custom_property_trigger_bind.sriv
Normal file
Binary file not shown.
Reference in New Issue
Block a user