Nested Inputs This PR adds support for setting NestedInput values from a parent artboard. We should support NestedBool, NestedNumber and NestedTrigger. After discussion with @alxgibsn there are at least 4 editor UI updates required to work together with this feature. - [x] Add a `public` boolean to StateMachineInput that allows you to specify whether the input should be available outside of its own artboard. This is presented in the motion inspector when a StateMachineInput is selected. - [x] Surfacing Nested Inputs in the Inputs panel alongside the Inputs for the current artboard. We also need a way to differentiate Nested Inputs from regular Inputs (possibly a folder-like hierarchy). - [x] Surfacing Nested Inputs in the Inputs list for a Listener. - [x] Surfacing Nested Inputs in the NestedArtboard StateMachine flyout. Looks like this work has already been done! - [x] Get NestedTrigger working Diffs= 45359b3e8 Nested Inputs (#6007) Co-authored-by: Philip Chung <philterdesign@gmail.com>
diff --git a/.rive_head b/.rive_head index fe1cfcf..eb5f98f 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -1a9dd7e97e623eb30dbec164ae0935b2ad848c1b +45359b3e81085b85fa517ec8e26ac273eb40ad04
diff --git a/dev/defs/animation/listener_input_change.json b/dev/defs/animation/listener_input_change.json index 445b3e4..006e50c 100644 --- a/dev/defs/animation/listener_input_change.json +++ b/dev/defs/animation/listener_input_change.json
@@ -17,6 +17,17 @@ "string": "inputid" }, "description": "Id of the StateMachineInput referenced." + }, + "nestedInputId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 400, + "string": "nestedinputid" + }, + "description": "Id of the NestedInput referenced if this is listening to a nested input." } } } \ No newline at end of file
diff --git a/dev/defs/animation/nested_trigger.json b/dev/defs/animation/nested_trigger.json index ea7f077..ab9255e 100644 --- a/dev/defs/animation/nested_trigger.json +++ b/dev/defs/animation/nested_trigger.json
@@ -4,5 +4,15 @@ "int": 122, "string": "nestedTrigger" }, - "extends": "animation/nested_input.json" + "extends": "animation/nested_input.json", + "properties": { + "fire": { + "type": "callback", + "animates": true, + "key": { + "int": 401, + "string": "fire" + } + } + } } \ No newline at end of file
diff --git a/dev/defs/animation/state_machine_input.json b/dev/defs/animation/state_machine_input.json index 51d3b0f..93d6e60 100644 --- a/dev/defs/animation/state_machine_input.json +++ b/dev/defs/animation/state_machine_input.json
@@ -5,5 +5,17 @@ "string": "statemachineinput" }, "abstract": true, - "extends": "animation/state_machine_component.json" + "extends": "animation/state_machine_component.json", + "properties": { + "public": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 402, + "string": "public" + }, + "description": "Determines whether this StateMachineInput will be exposed to parent artboards.", + "runtime": false + } + } } \ No newline at end of file
diff --git a/include/rive/animation/nested_bool.hpp b/include/rive/animation/nested_bool.hpp index 99f0dab..2900390 100644 --- a/include/rive/animation/nested_bool.hpp +++ b/include/rive/animation/nested_bool.hpp
@@ -7,6 +7,8 @@ class NestedBool : public NestedBoolBase { public: +protected: + void nestedValueChanged() override; }; } // namespace rive
diff --git a/include/rive/animation/nested_input.hpp b/include/rive/animation/nested_input.hpp index 158cf05..9d4970c 100644 --- a/include/rive/animation/nested_input.hpp +++ b/include/rive/animation/nested_input.hpp
@@ -1,5 +1,6 @@ #ifndef _RIVE_NESTED_INPUT_HPP_ #define _RIVE_NESTED_INPUT_HPP_ +#include "rive/animation/nested_state_machine.hpp" #include "rive/generated/animation/nested_input_base.hpp" #include <stdio.h> namespace rive @@ -7,6 +8,17 @@ class NestedInput : public NestedInputBase { public: +protected: + SMIInput* input() { + auto parent = this->parent(); + if (parent != nullptr && parent->is<NestedStateMachine>()) + { + StateMachineInstance* smInstance = parent->as<NestedStateMachine>()->stateMachineInstance(); + auto inputInstance = smInstance->input(this->inputId()); + return inputInstance; + } + return nullptr; + } }; } // namespace rive
diff --git a/include/rive/animation/nested_number.hpp b/include/rive/animation/nested_number.hpp index 5ae1053..6b51594 100644 --- a/include/rive/animation/nested_number.hpp +++ b/include/rive/animation/nested_number.hpp
@@ -7,6 +7,8 @@ class NestedNumber : public NestedNumberBase { public: +protected: + void nestedValueChanged() override; }; } // namespace rive
diff --git a/include/rive/animation/nested_state_machine.hpp b/include/rive/animation/nested_state_machine.hpp index 46790e7..da13c5a 100644 --- a/include/rive/animation/nested_state_machine.hpp +++ b/include/rive/animation/nested_state_machine.hpp
@@ -1,5 +1,6 @@ #ifndef _RIVE_NESTED_STATE_MACHINE_HPP_ #define _RIVE_NESTED_STATE_MACHINE_HPP_ +#include "rive/animation/state_machine_instance.hpp" #include "rive/generated/animation/nested_state_machine_base.hpp" #include "rive/math/vec2d.hpp" #include <memory>
diff --git a/include/rive/animation/nested_trigger.hpp b/include/rive/animation/nested_trigger.hpp index 7861dbd..5f1d67c 100644 --- a/include/rive/animation/nested_trigger.hpp +++ b/include/rive/animation/nested_trigger.hpp
@@ -7,6 +7,7 @@ class NestedTrigger : public NestedTriggerBase { public: + void fire(const CallbackData& value) override; }; } // namespace rive
diff --git a/include/rive/generated/animation/listener_input_change_base.hpp b/include/rive/generated/animation/listener_input_change_base.hpp index f0c982a..835bb76 100644 --- a/include/rive/generated/animation/listener_input_change_base.hpp +++ b/include/rive/generated/animation/listener_input_change_base.hpp
@@ -29,9 +29,11 @@ uint16_t coreType() const override { return typeKey; } static const uint16_t inputIdPropertyKey = 227; + static const uint16_t nestedInputIdPropertyKey = 400; private: uint32_t m_InputId = -1; + uint32_t m_NestedInputId = -1; public: inline uint32_t inputId() const { return m_InputId; } @@ -45,9 +47,21 @@ inputIdChanged(); } + inline uint32_t nestedInputId() const { return m_NestedInputId; } + void nestedInputId(uint32_t value) + { + if (m_NestedInputId == value) + { + return; + } + m_NestedInputId = value; + nestedInputIdChanged(); + } + void copy(const ListenerInputChangeBase& object) { m_InputId = object.m_InputId; + m_NestedInputId = object.m_NestedInputId; ListenerAction::copy(object); } @@ -58,12 +72,16 @@ case inputIdPropertyKey: m_InputId = CoreUintType::deserialize(reader); return true; + case nestedInputIdPropertyKey: + m_NestedInputId = CoreUintType::deserialize(reader); + return true; } return ListenerAction::deserialize(propertyKey, reader); } protected: virtual void inputIdChanged() {} + virtual void nestedInputIdChanged() {} }; } // namespace rive
diff --git a/include/rive/generated/animation/nested_trigger_base.hpp b/include/rive/generated/animation/nested_trigger_base.hpp index c489415..6fd3a5d 100644 --- a/include/rive/generated/animation/nested_trigger_base.hpp +++ b/include/rive/generated/animation/nested_trigger_base.hpp
@@ -1,6 +1,7 @@ #ifndef _RIVE_NESTED_TRIGGER_BASE_HPP_ #define _RIVE_NESTED_TRIGGER_BASE_HPP_ #include "rive/animation/nested_input.hpp" +#include "rive/core/field_types/core_callback_type.hpp" namespace rive { class NestedTriggerBase : public NestedInput @@ -28,6 +29,11 @@ uint16_t coreType() const override { return typeKey; } + static const uint16_t firePropertyKey = 401; + +public: + virtual void fire(const CallbackData& value) = 0; + Core* clone() const override; protected:
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index 5ca3da9..1f8987f 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp
@@ -452,6 +452,9 @@ case ListenerInputChangeBase::inputIdPropertyKey: object->as<ListenerInputChangeBase>()->inputId(value); break; + case ListenerInputChangeBase::nestedInputIdPropertyKey: + object->as<ListenerInputChangeBase>()->nestedInputId(value); + break; case AnimationStateBase::animationIdPropertyKey: object->as<AnimationStateBase>()->animationId(value); break; @@ -1141,6 +1144,18 @@ break; } } + static void setCallback(Core* object, int propertyKey, CallbackData value) + { + switch (propertyKey) + { + case NestedTriggerBase::firePropertyKey: + object->as<NestedTriggerBase>()->fire(value); + break; + case EventBase::triggerPropertyKey: + object->as<EventBase>()->trigger(value); + break; + } + } static void setColor(Core* object, int propertyKey, int value) { switch (propertyKey) @@ -1156,15 +1171,6 @@ break; } } - static void setCallback(Core* object, int propertyKey, CallbackData value) - { - switch (propertyKey) - { - case EventBase::triggerPropertyKey: - object->as<EventBase>()->trigger(value); - break; - } - } static std::string getString(Core* object, int propertyKey) { switch (propertyKey) @@ -1226,6 +1232,8 @@ return object->as<ListenerFireEventBase>()->eventId(); case ListenerInputChangeBase::inputIdPropertyKey: return object->as<ListenerInputChangeBase>()->inputId(); + case ListenerInputChangeBase::nestedInputIdPropertyKey: + return object->as<ListenerInputChangeBase>()->nestedInputId(); case AnimationStateBase::animationIdPropertyKey: return object->as<AnimationStateBase>()->animationId(); case NestedInputBase::inputIdPropertyKey: @@ -1736,6 +1744,7 @@ case SoloBase::activeComponentIdPropertyKey: case ListenerFireEventBase::eventIdPropertyKey: case ListenerInputChangeBase::inputIdPropertyKey: + case ListenerInputChangeBase::nestedInputIdPropertyKey: case AnimationStateBase::animationIdPropertyKey: case NestedInputBase::inputIdPropertyKey: case KeyedObjectBase::objectIdPropertyKey: @@ -1980,6 +1989,7 @@ { switch (propertyKey) { + case NestedTriggerBase::firePropertyKey: case EventBase::triggerPropertyKey: return true; default:
diff --git a/src/animation/listener_bool_change.cpp b/src/animation/listener_bool_change.cpp index e27c00c..ba9e2aa 100644 --- a/src/animation/listener_bool_change.cpp +++ b/src/animation/listener_bool_change.cpp
@@ -1,4 +1,7 @@ #include "rive/animation/listener_bool_change.hpp" +#include "rive/animation/nested_bool.hpp" +#include "rive/animation/nested_input.hpp" +#include "rive/animation/nested_state_machine.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/animation/state_machine_bool.hpp" #include "rive/animation/state_machine_input_instance.hpp" @@ -14,23 +17,49 @@ void ListenerBoolChange::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const { - auto inputInstance = stateMachineInstance->input(inputId()); - if (inputInstance == nullptr) + if (nestedInputId() != Core::emptyId) { - return; - } - // If it's not null, it must be our correct type (why we validate at load time). - auto boolInput = static_cast<SMIBool*>(inputInstance); - switch (value()) - { - case 0: - boolInput->value(false); - break; - case 1: - boolInput->value(true); - break; - default: - boolInput->value(!boolInput->value()); - break; + auto nestedInputInstance = stateMachineInstance->artboard()->resolve(nestedInputId()); + if (nestedInputInstance == nullptr) + { + return; + } + auto nestedBoolInput = static_cast<NestedBool*>(nestedInputInstance); + if (nestedBoolInput != nullptr) { + switch (value()) + { + case 0: + nestedBoolInput->nestedValue(false); + break; + case 1: + nestedBoolInput->nestedValue(true); + break; + default: + nestedBoolInput->nestedValue(!nestedBoolInput->nestedValue()); + break; + } + } + } else { + auto inputInstance = stateMachineInstance->input(inputId()); + if (inputInstance == nullptr) + { + return; + } + // If it's not null, it must be our correct type (why we validate at load time). + auto boolInput = static_cast<SMIBool*>(inputInstance); + if (boolInput != nullptr) { + switch (value()) + { + case 0: + boolInput->value(false); + break; + case 1: + boolInput->value(true); + break; + default: + boolInput->value(!boolInput->value()); + break; + } + } } }
diff --git a/src/animation/listener_number_change.cpp b/src/animation/listener_number_change.cpp index 3f8af9b..3b3ae43 100644 --- a/src/animation/listener_number_change.cpp +++ b/src/animation/listener_number_change.cpp
@@ -1,4 +1,6 @@ #include "rive/animation/listener_number_change.hpp" +#include "rive/animation/nested_number.hpp" +#include "rive/animation/nested_state_machine.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/animation/state_machine_number.hpp" #include "rive/animation/state_machine_input_instance.hpp" @@ -15,12 +17,27 @@ void ListenerNumberChange::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const { - auto inputInstance = stateMachineInstance->input(inputId()); - if (inputInstance == nullptr) + if (nestedInputId() != Core::emptyId) { - return; + auto nestedInputInstance = stateMachineInstance->artboard()->resolve(nestedInputId()); + if (nestedInputInstance == nullptr) + { + return; + } + auto nestedNumberInput = static_cast<NestedNumber*>(nestedInputInstance); + if (nestedNumberInput != nullptr) { + nestedNumberInput->nestedValue(value()); + } + } else { + auto inputInstance = stateMachineInstance->input(inputId()); + if (inputInstance == nullptr) + { + return; + } + // If it's not null, it must be our correct type (why we validate at load time). + auto numberInput = static_cast<SMINumber*>(inputInstance); + if (numberInput != nullptr) { + numberInput->value(value()); + } } - // If it's not null, it must be our correct type (why we validate at load time). - auto numberInput = static_cast<SMINumber*>(inputInstance); - numberInput->value(value()); }
diff --git a/src/animation/listener_trigger_change.cpp b/src/animation/listener_trigger_change.cpp index 3dff886..8b6ac21 100644 --- a/src/animation/listener_trigger_change.cpp +++ b/src/animation/listener_trigger_change.cpp
@@ -1,7 +1,10 @@ #include "rive/animation/listener_trigger_change.hpp" +#include "rive/animation/nested_trigger.hpp" +#include "rive/animation/nested_state_machine.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/animation/state_machine_trigger.hpp" #include "rive/animation/state_machine_input_instance.hpp" +#include "rive/core/field_types/core_callback_type.hpp" using namespace rive; @@ -16,12 +19,27 @@ void ListenerTriggerChange::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const { - auto inputInstance = stateMachineInstance->input(inputId()); - if (inputInstance == nullptr) + if (nestedInputId() != Core::emptyId) { - return; + auto nestedInputInstance = stateMachineInstance->artboard()->resolve(nestedInputId()); + if (nestedInputInstance == nullptr) + { + return; + } + auto nestedTriggerInput = static_cast<NestedTrigger*>(nestedInputInstance); + if (nestedTriggerInput != nullptr) { + nestedTriggerInput->fire(CallbackData(stateMachineInstance, 0)); + } + } else { + auto inputInstance = stateMachineInstance->input(inputId()); + if (inputInstance == nullptr) + { + return; + } + // If it's not null, it must be our correct type (why we validate at load time). + auto triggerInput = static_cast<SMITrigger*>(inputInstance); + if (triggerInput != nullptr) { + triggerInput->fire(); + } } - // If it's not null, it must be our correct type (why we validate at load time). - auto triggerInput = static_cast<SMITrigger*>(inputInstance); - triggerInput->fire(); }
diff --git a/src/animation/nested_bool.cpp b/src/animation/nested_bool.cpp new file mode 100644 index 0000000..cf4110d --- /dev/null +++ b/src/animation/nested_bool.cpp
@@ -0,0 +1,18 @@ +#include "rive/animation/nested_bool.hpp" +#include "rive/animation/nested_state_machine.hpp" +#include "rive/animation/state_machine_input_instance.hpp" +#include "rive/container_component.hpp" + +using namespace rive; +class StateMachineInstance; + +void NestedBool::nestedValueChanged() { + auto inputInstance = input(); + if (inputInstance != nullptr) + { + auto boolInput = static_cast<SMIBool*>(inputInstance); + if (boolInput != nullptr) { + boolInput->value(nestedValue()); + } + } +} \ No newline at end of file
diff --git a/src/animation/nested_number.cpp b/src/animation/nested_number.cpp new file mode 100644 index 0000000..6628b91 --- /dev/null +++ b/src/animation/nested_number.cpp
@@ -0,0 +1,18 @@ +#include "rive/animation/nested_number.hpp" +#include "rive/animation/nested_state_machine.hpp" +#include "rive/animation/state_machine_input_instance.hpp" +#include "rive/container_component.hpp" + +using namespace rive; +class StateMachineInstance; + +void NestedNumber::nestedValueChanged() { + auto inputInstance = input(); + if (inputInstance != nullptr) + { + auto numInput = static_cast<SMINumber*>(inputInstance); + if (numInput != nullptr) { + numInput->value(nestedValue()); + } + } +} \ No newline at end of file
diff --git a/src/animation/nested_trigger.cpp b/src/animation/nested_trigger.cpp new file mode 100644 index 0000000..f590f94 --- /dev/null +++ b/src/animation/nested_trigger.cpp
@@ -0,0 +1,19 @@ +#include "rive/animation/nested_trigger.hpp" +#include "rive/animation/nested_state_machine.hpp" +#include "rive/animation/state_machine_input_instance.hpp" +#include "rive/container_component.hpp" + +using namespace rive; +class StateMachineInstance; + +void NestedTrigger::fire(const CallbackData& value) { + auto inputInstance = input(); + if (inputInstance != nullptr) + { + auto numInput = static_cast<SMITrigger*>(inputInstance); + if (numInput != nullptr) { + numInput->fire(); + } + } + +} \ No newline at end of file