Xxxx support target align from position A lot of files changed, but they're mostly boilerplate code and changes to the API of the actions. This PR adds support for using Align Target without moving the aligned target to the pointer position. Instead, it moves the element from its starting point following the mouse position. It adds a new boolean property to enable the feature. In order to achieve this, it's necessary to provide to all actions the current position and the previous position so the action can calculate the delta. Diffs= a55f1ffb6 Xxxx support target align from position (#7154) Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head index 8e33160..ca0ca4d 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -7d0125c9256d329a11844184e6cd1244f3496012 +a55f1ffb6cabe9b918fa0b8fd1ad11b4d656ae1c
diff --git a/dev/defs/animation/listener_align_target.json b/dev/defs/animation/listener_align_target.json index b34168f..699b160 100644 --- a/dev/defs/animation/listener_align_target.json +++ b/dev/defs/animation/listener_align_target.json
@@ -16,6 +16,15 @@ "string": "targetid" }, "description": "Identifier used to track the object use as a target fo this listener action." + }, + "preserveOffset": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 541, + "string": "preserveoffset" + }, + "description": "Whether to preserve offset between mouse position and target position." } } } \ No newline at end of file
diff --git a/include/rive/animation/listener_action.hpp b/include/rive/animation/listener_action.hpp index 370a5cc..691ab0f 100644 --- a/include/rive/animation/listener_action.hpp +++ b/include/rive/animation/listener_action.hpp
@@ -10,7 +10,9 @@ { public: StatusCode import(ImportStack& importStack) override; - virtual void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const = 0; + virtual void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const = 0; }; } // namespace rive
diff --git a/include/rive/animation/listener_align_target.hpp b/include/rive/animation/listener_align_target.hpp index dd71108..2a150d6 100644 --- a/include/rive/animation/listener_align_target.hpp +++ b/include/rive/animation/listener_align_target.hpp
@@ -7,7 +7,9 @@ class ListenerAlignTarget : public ListenerAlignTargetBase { public: - void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override; + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; }; } // namespace rive
diff --git a/include/rive/animation/listener_bool_change.hpp b/include/rive/animation/listener_bool_change.hpp index df0b762..a6f25c2 100644 --- a/include/rive/animation/listener_bool_change.hpp +++ b/include/rive/animation/listener_bool_change.hpp
@@ -10,7 +10,9 @@ public: bool validateInputType(const StateMachineInput* input) const override; bool validateNestedInputType(const NestedInput* input) const override; - void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override; + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; }; } // namespace rive
diff --git a/include/rive/animation/listener_fire_event.hpp b/include/rive/animation/listener_fire_event.hpp index 66c4850..4e4a7d2 100644 --- a/include/rive/animation/listener_fire_event.hpp +++ b/include/rive/animation/listener_fire_event.hpp
@@ -7,7 +7,9 @@ class ListenerFireEvent : public ListenerFireEventBase { public: - void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override; + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; }; } // namespace rive
diff --git a/include/rive/animation/listener_number_change.hpp b/include/rive/animation/listener_number_change.hpp index d76cc0e..613d7d3 100644 --- a/include/rive/animation/listener_number_change.hpp +++ b/include/rive/animation/listener_number_change.hpp
@@ -10,7 +10,9 @@ public: bool validateInputType(const StateMachineInput* input) const override; bool validateNestedInputType(const NestedInput* input) const override; - void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override; + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; }; } // namespace rive
diff --git a/include/rive/animation/listener_trigger_change.hpp b/include/rive/animation/listener_trigger_change.hpp index 7a9c595..005f472 100644 --- a/include/rive/animation/listener_trigger_change.hpp +++ b/include/rive/animation/listener_trigger_change.hpp
@@ -10,7 +10,9 @@ public: bool validateInputType(const StateMachineInput* input) const override; bool validateNestedInputType(const NestedInput* input) const override; - void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override; + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; }; } // namespace rive
diff --git a/include/rive/animation/state_machine_listener.hpp b/include/rive/animation/state_machine_listener.hpp index 88be3c0..244eb27 100644 --- a/include/rive/animation/state_machine_listener.hpp +++ b/include/rive/animation/state_machine_listener.hpp
@@ -31,7 +31,9 @@ StatusCode onAddedClean(CoreContext* context) override; const std::vector<uint32_t>& hitShapeIds() const { return m_HitShapesIds; } - void performChanges(StateMachineInstance* stateMachineInstance, Vec2D position) const; + void performChanges(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const; }; } // namespace rive
diff --git a/include/rive/generated/animation/listener_align_target_base.hpp b/include/rive/generated/animation/listener_align_target_base.hpp index 79c0cfc..b7e5800 100644 --- a/include/rive/generated/animation/listener_align_target_base.hpp +++ b/include/rive/generated/animation/listener_align_target_base.hpp
@@ -1,6 +1,7 @@ #ifndef _RIVE_LISTENER_ALIGN_TARGET_BASE_HPP_ #define _RIVE_LISTENER_ALIGN_TARGET_BASE_HPP_ #include "rive/animation/listener_action.hpp" +#include "rive/core/field_types/core_bool_type.hpp" #include "rive/core/field_types/core_uint_type.hpp" namespace rive { @@ -29,9 +30,11 @@ uint16_t coreType() const override { return typeKey; } static const uint16_t targetIdPropertyKey = 240; + static const uint16_t preserveOffsetPropertyKey = 541; private: uint32_t m_TargetId = 0; + bool m_PreserveOffset = false; public: inline uint32_t targetId() const { return m_TargetId; } @@ -45,10 +48,22 @@ targetIdChanged(); } + inline bool preserveOffset() const { return m_PreserveOffset; } + void preserveOffset(bool value) + { + if (m_PreserveOffset == value) + { + return; + } + m_PreserveOffset = value; + preserveOffsetChanged(); + } + Core* clone() const override; void copy(const ListenerAlignTargetBase& object) { m_TargetId = object.m_TargetId; + m_PreserveOffset = object.m_PreserveOffset; ListenerAction::copy(object); } @@ -59,12 +74,16 @@ case targetIdPropertyKey: m_TargetId = CoreUintType::deserialize(reader); return true; + case preserveOffsetPropertyKey: + m_PreserveOffset = CoreBoolType::deserialize(reader); + return true; } return ListenerAction::deserialize(propertyKey, reader); } protected: virtual void targetIdChanged() {} + virtual void preserveOffsetChanged() {} }; } // namespace rive
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index 0b077aa..a0c8df9 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp
@@ -1138,6 +1138,9 @@ case KeyFrameBoolBase::valuePropertyKey: object->as<KeyFrameBoolBase>()->value(value); break; + case ListenerAlignTargetBase::preserveOffsetPropertyKey: + object->as<ListenerAlignTargetBase>()->preserveOffset(value); + break; case NestedBoolBase::nestedValuePropertyKey: object->as<NestedBoolBase>()->nestedValue(value); break; @@ -1720,6 +1723,8 @@ return object->as<NestedSimpleAnimationBase>()->isPlaying(); case KeyFrameBoolBase::valuePropertyKey: return object->as<KeyFrameBoolBase>()->value(); + case ListenerAlignTargetBase::preserveOffsetPropertyKey: + return object->as<ListenerAlignTargetBase>()->preserveOffset(); case NestedBoolBase::nestedValuePropertyKey: return object->as<NestedBoolBase>()->nestedValue(); case LinearAnimationBase::enableWorkAreaPropertyKey: @@ -2013,6 +2018,7 @@ case FollowPathConstraintBase::offsetPropertyKey: case NestedSimpleAnimationBase::isPlayingPropertyKey: case KeyFrameBoolBase::valuePropertyKey: + case ListenerAlignTargetBase::preserveOffsetPropertyKey: case NestedBoolBase::nestedValuePropertyKey: case LinearAnimationBase::enableWorkAreaPropertyKey: case LinearAnimationBase::quantizePropertyKey:
diff --git a/src/animation/listener_align_target.cpp b/src/animation/listener_align_target.cpp index c0a573a..415321e 100644 --- a/src/animation/listener_align_target.cpp +++ b/src/animation/listener_align_target.cpp
@@ -5,7 +5,9 @@ using namespace rive; -void ListenerAlignTarget::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const +void ListenerAlignTarget::perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const { auto coreTarget = stateMachineInstance->artboard()->resolve(targetId()); if (coreTarget == nullptr || !coreTarget->is<Node>()) @@ -19,8 +21,18 @@ { return; } + if (preserveOffset()) + { - auto localPosition = inverse * position; - target->x(localPosition.x); - target->y(localPosition.y); + auto localPosition = inverse * position; + auto prevLocalPosition = inverse * previousPosition; + target->x(target->x() + localPosition.x - prevLocalPosition.x); + target->y(target->y() + localPosition.y - prevLocalPosition.y); + } + else + { + auto localPosition = inverse * position; + target->x(localPosition.x); + target->y(localPosition.y); + } }
diff --git a/src/animation/listener_bool_change.cpp b/src/animation/listener_bool_change.cpp index b86e0f8..e84808c 100644 --- a/src/animation/listener_bool_change.cpp +++ b/src/animation/listener_bool_change.cpp
@@ -23,7 +23,9 @@ return input == nullptr || input->is<NestedBool>(); } -void ListenerBoolChange::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const +void ListenerBoolChange::perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const { if (nestedInputId() != Core::emptyId) {
diff --git a/src/animation/listener_fire_event.cpp b/src/animation/listener_fire_event.cpp index 41e8dc6..bd6d074 100644 --- a/src/animation/listener_fire_event.cpp +++ b/src/animation/listener_fire_event.cpp
@@ -4,7 +4,9 @@ using namespace rive; -void ListenerFireEvent::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const +void ListenerFireEvent::perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const { auto coreEvent = stateMachineInstance->artboard()->resolve(eventId()); if (coreEvent == nullptr || !coreEvent->is<Event>())
diff --git a/src/animation/listener_number_change.cpp b/src/animation/listener_number_change.cpp index ca1d5c2..61cac0c 100644 --- a/src/animation/listener_number_change.cpp +++ b/src/animation/listener_number_change.cpp
@@ -24,7 +24,9 @@ return input == nullptr || input->is<NestedNumber>(); } -void ListenerNumberChange::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const +void ListenerNumberChange::perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const { if (nestedInputId() != Core::emptyId) {
diff --git a/src/animation/listener_trigger_change.cpp b/src/animation/listener_trigger_change.cpp index a7b0579..2832ff8 100644 --- a/src/animation/listener_trigger_change.cpp +++ b/src/animation/listener_trigger_change.cpp
@@ -26,7 +26,8 @@ } void ListenerTriggerChange::perform(StateMachineInstance* stateMachineInstance, - Vec2D position) const + Vec2D position, + Vec2D previousPosition) const { if (nestedInputId() != Core::emptyId) {
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp index 1719bbe..c0f5f58 100644 --- a/src/animation/state_machine_instance.cpp +++ b/src/animation/state_machine_instance.cpp
@@ -411,6 +411,7 @@ {} bool isHovered = false; float hitRadius = 2; + Vec2D previousPosition; std::vector<const StateMachineListener*> listeners; HitResult processEvent(Vec2D position, ListenerType hitType, bool canHit) override { @@ -423,6 +424,11 @@ bool isOver = canHit ? shape->hitTest(hitArea) : false; bool hoverChange = isHovered != isOver; isHovered = isOver; + if (hoverChange && isHovered) + { + previousPosition.x = position.x; + previousPosition.y = position.y; + } // // iterate all listeners associated with this hit shape for (auto listener : listeners) @@ -433,21 +439,23 @@ { if (isOver && listener->listenerType() == ListenerType::enter) { - listener->performChanges(m_stateMachineInstance, position); + listener->performChanges(m_stateMachineInstance, position, previousPosition); m_stateMachineInstance->markNeedsAdvance(); } else if (!isOver && listener->listenerType() == ListenerType::exit) { - listener->performChanges(m_stateMachineInstance, position); + listener->performChanges(m_stateMachineInstance, position, previousPosition); m_stateMachineInstance->markNeedsAdvance(); } } if (isOver && hitType == listener->listenerType()) { - listener->performChanges(m_stateMachineInstance, position); + listener->performChanges(m_stateMachineInstance, position, previousPosition); m_stateMachineInstance->markNeedsAdvance(); } } + previousPosition.x = position.x; + previousPosition.y = position.y; return isOver ? shape->isTargetOpaque() ? HitResult::hitOpaque : HitResult::hit : HitResult::none; } @@ -891,7 +899,7 @@ auto listenerEvent = sourceArtboard->resolve(listener->eventId()); if (listenerEvent == event.event()) { - listener->performChanges(this, Vec2D()); + listener->performChanges(this, Vec2D(), Vec2D()); break; } }
diff --git a/src/animation/state_machine_listener.cpp b/src/animation/state_machine_listener.cpp index 3e51892..f943f82 100644 --- a/src/animation/state_machine_listener.cpp +++ b/src/animation/state_machine_listener.cpp
@@ -75,10 +75,11 @@ } void StateMachineListener::performChanges(StateMachineInstance* stateMachineInstance, - Vec2D position) const + Vec2D position, + Vec2D previousPosition) const { for (auto& action : m_Actions) { - action->perform(stateMachineInstance, position); + action->perform(stateMachineInstance, position, previousPosition); } } \ No newline at end of file
diff --git a/test/assets/align_target.riv b/test/assets/align_target.riv new file mode 100644 index 0000000..3849239 --- /dev/null +++ b/test/assets/align_target.riv Binary files differ
diff --git a/test/listener_align_target_test.cpp b/test/listener_align_target_test.cpp new file mode 100644 index 0000000..f41dbab --- /dev/null +++ b/test/listener_align_target_test.cpp
@@ -0,0 +1,82 @@ +/* + * Copyright 2022 Rive + */ + +#include <rive/math/aabb.hpp> +#include <rive/animation/state_machine_instance.hpp> +#include <rive/animation/state_machine_input_instance.hpp> +#include <rive/animation/nested_state_machine.hpp> +#include <rive/shapes/ellipse.hpp> +#include <rive/shapes/shape.hpp> +#include "rive_file_reader.hpp" + +#include <catch.hpp> +#include <cstdio> + +using namespace rive; + +TEST_CASE("align target with preserve offset off test", "[listener_align]") +{ + // The circle starts at coords 100, 100 + // Once the pointer move has acted, the new coords should be 100, 51 + auto file = ReadRiveFile("../../test/assets/align_target.riv"); + + auto artboard = file->artboard("preserve-inactive"); + auto artboardInstance = artboard->instance(); + auto stateMachine = artboard->stateMachine("align-state-machine"); + + REQUIRE(artboardInstance != nullptr); + REQUIRE(artboardInstance->stateMachineCount() == 1); + + REQUIRE(stateMachine != nullptr); + + rive::StateMachineInstance* stateMachineInstance = + new rive::StateMachineInstance(stateMachine, artboardInstance.get()); + + artboardInstance->advance(0.0f); + stateMachineInstance->advanceAndApply(0.0f); + REQUIRE(stateMachineInstance->needsAdvance() == true); + stateMachineInstance->advance(0.0f); + auto circle = stateMachineInstance->artboard()->find<rive::Shape>("circle"); + REQUIRE(circle != nullptr); + stateMachineInstance->pointerMove(rive::Vec2D(100.0f, 50.0f)); + stateMachineInstance->pointerMove(rive::Vec2D(100.0f, 51.0f)); + stateMachineInstance->advanceAndApply(1.0f); + stateMachineInstance->advance(0.0f); + REQUIRE(circle->x() == 100.0f); + REQUIRE(circle->y() == 51.0f); + delete stateMachineInstance; +} + +TEST_CASE("align target preserve offset test", "[listener_align]") +{ + // The circle starts at coords 100, 100 + // Once the pointer move has acted, the new coords should be 100, 101 + auto file = ReadRiveFile("../../test/assets/align_target.riv"); + + auto artboard = file->artboard("preserve-active"); + auto artboardInstance = artboard->instance(); + auto stateMachine = artboard->stateMachine("align-state-machine"); + + REQUIRE(artboardInstance != nullptr); + REQUIRE(artboardInstance->stateMachineCount() == 1); + + REQUIRE(stateMachine != nullptr); + + rive::StateMachineInstance* stateMachineInstance = + new rive::StateMachineInstance(stateMachine, artboardInstance.get()); + + artboardInstance->advance(0.0f); + stateMachineInstance->advanceAndApply(0.0f); + REQUIRE(stateMachineInstance->needsAdvance() == true); + stateMachineInstance->advance(0.0f); + auto circle = stateMachineInstance->artboard()->find<rive::Shape>("circle"); + REQUIRE(circle != nullptr); + stateMachineInstance->pointerMove(rive::Vec2D(100.0f, 50.0f)); + stateMachineInstance->pointerMove(rive::Vec2D(100.0f, 51.0f)); + stateMachineInstance->advanceAndApply(1.0f); + stateMachineInstance->advance(0.0f); + REQUIRE(circle->x() == 100.0f); + REQUIRE(circle->y() == 101.0f); + delete stateMachineInstance; +} \ No newline at end of file