diff --git a/include/hyprutils/animation/AnimatedVariable.hpp b/include/hyprutils/animation/AnimatedVariable.hpp index e413d16..385c586 100644 --- a/include/hyprutils/animation/AnimatedVariable.hpp +++ b/include/hyprutils/animation/AnimatedVariable.hpp @@ -1,5 +1,8 @@ #pragma once +#include "../memory/WeakPtr.hpp" +#include "hyprutils/memory/SharedPtr.hpp" + #include #include @@ -12,27 +15,27 @@ namespace Hyprutils { Config properties need to have a static lifetime to allow for config reload. */ struct SAnimationPropertyConfig { - bool overridden = true; + bool overridden = true; - std::string internalBezier = ""; - std::string internalStyle = ""; - float internalSpeed = 0.f; - int internalEnabled = -1; + std::string internalBezier = ""; + std::string internalStyle = ""; + float internalSpeed = 0.f; + int internalEnabled = -1; - SAnimationPropertyConfig* pValues = nullptr; - SAnimationPropertyConfig* pParentAnimation = nullptr; + Memory::CWeakPointer pValues; + Memory::CWeakPointer pParentAnimation; }; /* A base class for animated variables. */ class CBaseAnimatedVariable { public: - using CallbackFun = std::function; + using CallbackFun = std::function thisptr)>; CBaseAnimatedVariable() { ; // m_bDummy = true; }; - void create(CAnimationManager* p, int typeInfo); + void create(CAnimationManager*, int, Memory::CSharedPointer); void connectToActive(); void disconnectFromActive(); @@ -48,11 +51,11 @@ namespace Hyprutils { CBaseAnimatedVariable& operator=(const CBaseAnimatedVariable&) = delete; CBaseAnimatedVariable& operator=(CBaseAnimatedVariable&&) = delete; - void setConfig(SAnimationPropertyConfig* pConfig) { + void setConfig(Memory::CSharedPointer pConfig) { m_pConfig = pConfig; } - SAnimationPropertyConfig* getConfig() const { + Memory::CWeakPointer getConfig() const { return m_pConfig; } @@ -100,23 +103,25 @@ namespace Hyprutils { protected: friend class CAnimationManager; - bool m_bIsConnectedToActive = false; - bool m_bIsBeingAnimated = false; + bool m_bIsConnectedToActive = false; + bool m_bIsBeingAnimated = false; + + Memory::CWeakPointer m_pSelf; private: - SAnimationPropertyConfig* m_pConfig; + Memory::CWeakPointer m_pConfig; - std::chrono::steady_clock::time_point animationBegin; + std::chrono::steady_clock::time_point animationBegin; - bool m_bDummy = true; + bool m_bDummy = true; - CAnimationManager* m_pAnimationManager; - bool m_bRemoveEndAfterRan = true; - bool m_bRemoveBeginAfterRan = true; + CAnimationManager* m_pAnimationManager; + bool m_bRemoveEndAfterRan = true; + bool m_bRemoveBeginAfterRan = true; - CallbackFun m_fEndCallback; - CallbackFun m_fBeginCallback; - CallbackFun m_fUpdateCallback; + CallbackFun m_fEndCallback; + CallbackFun m_fBeginCallback; + CallbackFun m_fUpdateCallback; }; /* This concept represents the minimum requirement for a type to be used with CGenericAnimatedVariable */ @@ -138,12 +143,13 @@ namespace Hyprutils { public: CGenericAnimatedVariable() = default; - void create(const int typeInfo, const VarType& initialValue, CAnimationManager* pAnimationManager) { + void create(const int typeInfo, CAnimationManager* pAnimationManager, Memory::CSharedPointer> pSelf, + const VarType& initialValue) { m_Begun = initialValue; m_Value = initialValue; m_Goal = initialValue; - CBaseAnimatedVariable::create(pAnimationManager, typeInfo); + CBaseAnimatedVariable::create(pAnimationManager, typeInfo, pSelf); } CGenericAnimatedVariable(const CGenericAnimatedVariable&) = delete; diff --git a/include/hyprutils/animation/AnimationManager.hpp b/include/hyprutils/animation/AnimationManager.hpp index aa74567..d1bea73 100644 --- a/include/hyprutils/animation/AnimationManager.hpp +++ b/include/hyprutils/animation/AnimationManager.hpp @@ -3,7 +3,7 @@ #include "./BezierCurve.hpp" #include "./AnimatedVariable.hpp" #include "../math/Vector2D.hpp" -#include "../memory/SharedPtr.hpp" +#include "../memory/WeakPtr.hpp" #include #include @@ -13,7 +13,6 @@ using namespace Hyprutils::Math; namespace Hyprutils { namespace Animation { - /* A class for managing bezier curves and variables that are being animated. */ class CAnimationManager { public: @@ -33,7 +32,7 @@ namespace Hyprutils { std::unordered_map> getAllBeziers(); - std::vector m_vActiveAnimatedVariables; + std::vector> m_vActiveAnimatedVariables; private: std::unordered_map> m_mBezierCurves; diff --git a/src/animation/AnimatedVariable.cpp b/src/animation/AnimatedVariable.cpp index 3103b2d..9eb21f4 100644 --- a/src/animation/AnimatedVariable.cpp +++ b/src/animation/AnimatedVariable.cpp @@ -1,11 +1,17 @@ #include #include +#include using namespace Hyprutils::Animation; +using namespace Hyprutils::Memory; -void CBaseAnimatedVariable::create(Hyprutils::Animation::CAnimationManager* pAnimationManager, int typeInfo) { +#define SP CSharedPointer +#define WP CWeakPointer + +void CBaseAnimatedVariable::create(Hyprutils::Animation::CAnimationManager* pAnimationManager, int typeInfo, SP pSelf) { m_pAnimationManager = pAnimationManager; m_Type = typeInfo; + m_pSelf = pSelf; m_bDummy = false; } @@ -16,7 +22,7 @@ void CBaseAnimatedVariable::connectToActive() { m_pAnimationManager->scheduleTick(); // otherwise the animation manager will never pick this up if (!m_bIsConnectedToActive) - m_pAnimationManager->m_vActiveAnimatedVariables.push_back(this); + m_pAnimationManager->m_vActiveAnimatedVariables.push_back(m_pSelf); m_bIsConnectedToActive = true; } @@ -24,22 +30,39 @@ void CBaseAnimatedVariable::disconnectFromActive() { if (!m_pAnimationManager) return; - std::erase_if(m_pAnimationManager->m_vActiveAnimatedVariables, [&](const auto& other) { return other == this; }); + std::erase_if(m_pAnimationManager->m_vActiveAnimatedVariables, [&](const auto& other) { return other == m_pSelf; }); m_bIsConnectedToActive = false; } bool Hyprutils::Animation::CBaseAnimatedVariable::enabled() const { - return m_pConfig ? m_pConfig->pValues->internalEnabled : false; + if (const auto PCONFIG = m_pConfig.lock()) { + const auto PVALUES = PCONFIG->pValues.lock(); + return PVALUES ? PVALUES->internalEnabled : false; + } + + return false; } const std::string& CBaseAnimatedVariable::getBezierName() const { static constexpr const std::string DEFAULTBEZIERNAME = "default"; - return m_pConfig ? m_pConfig->pValues->internalBezier : DEFAULTBEZIERNAME; + + if (const auto PCONFIG = m_pConfig.lock()) { + const auto PVALUES = PCONFIG->pValues.lock(); + return PVALUES ? PVALUES->internalBezier : DEFAULTBEZIERNAME; + } + + return DEFAULTBEZIERNAME; } const std::string& CBaseAnimatedVariable::getStyle() const { static constexpr const std::string DEFAULTSTYLE = ""; - return m_pConfig ? m_pConfig->pValues->internalStyle : DEFAULTSTYLE; + + if (const auto PCONFIG = m_pConfig.lock()) { + const auto PVALUES = PCONFIG->pValues.lock(); + return PVALUES ? PVALUES->internalStyle : DEFAULTSTYLE; + } + + return DEFAULTSTYLE; } float CBaseAnimatedVariable::getPercent() const { @@ -51,12 +74,22 @@ float CBaseAnimatedVariable::getCurveValue() const { if (!m_bIsBeingAnimated || !m_pAnimationManager) return 1.f; - const auto SPENT = getPercent(); + std::string bezierName = ""; + if (const auto PCONFIG = m_pConfig.lock()) { + const auto PVALUES = PCONFIG->pValues.lock(); + if (PVALUES) + bezierName = PVALUES->internalBezier; + } + const auto BEZIER = m_pAnimationManager->getBezier(bezierName); + if (!BEZIER) + return 1.f; + + const auto SPENT = getPercent(); if (SPENT >= 1.f) return 1.f; - return m_pAnimationManager->getBezier(m_pConfig->pValues->internalBezier)->getYForPoint(SPENT); + return BEZIER->getYForPoint(SPENT); } bool CBaseAnimatedVariable::ok() const { @@ -65,7 +98,7 @@ bool CBaseAnimatedVariable::ok() const { void CBaseAnimatedVariable::onUpdate() { if (m_fUpdateCallback) - m_fUpdateCallback(this); + m_fUpdateCallback(m_pSelf); } void CBaseAnimatedVariable::setCallbackOnEnd(CallbackFun func, bool remove) { @@ -100,7 +133,7 @@ void CBaseAnimatedVariable::onAnimationEnd() { if (m_fEndCallback) { /* loading m_bRemoveEndAfterRan before calling the callback allows the callback to delete this animation safely if it is false. */ auto removeEndCallback = m_bRemoveEndAfterRan; - m_fEndCallback(this); + m_fEndCallback(m_pSelf); if (removeEndCallback) m_fEndCallback = nullptr; // reset } @@ -112,7 +145,7 @@ void CBaseAnimatedVariable::onAnimationBegin() { connectToActive(); if (m_fBeginCallback) { - m_fBeginCallback(this); + m_fBeginCallback(m_pSelf); if (m_bRemoveBeginAfterRan) m_fBeginCallback = nullptr; // reset } diff --git a/src/animation/AnimationManager.cpp b/src/animation/AnimationManager.cpp index 4bd4ffd..7506f62 100644 --- a/src/animation/AnimationManager.cpp +++ b/src/animation/AnimationManager.cpp @@ -37,14 +37,18 @@ bool CAnimationManager::shouldTickForNext() { } void CAnimationManager::tickDone() { - std::vector active; + std::vector> active; // avoid reallocations active.reserve(m_vActiveAnimatedVariables.size()); for (auto const& av : m_vActiveAnimatedVariables) { - if (av->ok() && av->isBeingAnimated()) + const auto PAV = av.lock(); + if (!PAV) + continue; + + if (PAV->ok() && PAV->isBeingAnimated()) active.push_back(av); else - av->m_bIsConnectedToActive = false; + PAV->m_bIsConnectedToActive = false; } m_vActiveAnimatedVariables = std::move(active); diff --git a/tests/animation.cpp b/tests/animation.cpp index b1ef9b9..5b3ca61 100644 --- a/tests/animation.cpp +++ b/tests/animation.cpp @@ -1,15 +1,26 @@ #include #include +#include #include "shared.hpp" +#define SP CSharedPointer +#define WP CWeakPointer + using namespace Hyprutils::Animation; using namespace Hyprutils::Math; +using namespace Hyprutils::Memory; class EmtpyContext {}; template using CAnimatedVariable = CGenericAnimatedVariable; +template +using PANIMVAR = SP>; + +template +using PANIMVARREF = WP>; + enum eAVTypes { INT = 1, TEST, @@ -26,30 +37,31 @@ struct SomeTestType { } }; -#define INITANIMCFG(name) animationConfig[name] = {} -#define CREATEANIMCFG(name, parent) animationConfig[name] = {false, "", "", 0.f, -1, &animationConfig["global"], &animationConfig[parent]} +#define INITANIMCFG(name) animationConfig[name] = makeShared(); +#define CREATEANIMCFG(name, parent) *animationConfig[name] = {false, "", "", 0.f, -1, animationConfig["global"], animationConfig[parent]} -std::unordered_map animationConfig; +std::unordered_map> animationConfig; class CMyAnimationManager : public CAnimationManager { public: void tick() { for (auto const& av : m_vActiveAnimatedVariables) { - if (!av->ok()) + const auto PAV = av.lock(); + if (!PAV || !PAV->ok()) continue; - const auto SPENT = av->getPercent(); - const auto PBEZIER = getBezier(av->getBezierName()); + const auto SPENT = PAV->getPercent(); + const auto PBEZIER = getBezier(PAV->getBezierName()); const auto POINTY = PBEZIER->getYForPoint(SPENT); - if (POINTY >= 1.f || !av->enabled()) { - av->warp(); + if (POINTY >= 1.f || !PAV->enabled()) { + PAV->warp(); continue; } - switch (av->m_Type) { + switch (PAV->m_Type) { case eAVTypes::INT: { - auto* avInt = dynamic_cast*>(av); + auto avInt = dynamic_cast*>(PAV.get()); if (!avInt) std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET; @@ -57,7 +69,7 @@ class CMyAnimationManager : public CAnimationManager { avInt->value() = avInt->begun() + (DELTA * POINTY); } break; case eAVTypes::TEST: { - auto* avCustom = dynamic_cast*>(av); + auto avCustom = dynamic_cast*>(PAV.get()); if (!avCustom) std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET; @@ -75,11 +87,14 @@ class CMyAnimationManager : public CAnimationManager { tickDone(); } - template - void addAnimation(const AnimType& v, CAnimatedVariable& av, const std::string& animationConfigName) { - constexpr const eAVTypes EAVTYPE = std::is_same_v ? eAVTypes::INT : eAVTypes::TEST; - av.create(EAVTYPE, v, static_cast(this)); - av.setConfig(&animationConfig[animationConfigName]); + template + void createAnimation(const VarType& v, PANIMVAR& av, const std::string& animationConfigName) { + constexpr const eAVTypes EAVTYPE = std::is_same_v ? eAVTypes::INT : eAVTypes::TEST; + const auto PAV = makeShared>(); + + PAV->create(EAVTYPE, static_cast(this), PAV, v); + PAV->setConfig(animationConfig[animationConfigName]); + av = std::move(PAV); } virtual void scheduleTick() { @@ -96,64 +111,63 @@ CMyAnimationManager gAnimationManager; class Subject { public: Subject(const int& a, const int& b) { - gAnimationManager.addAnimation(a, m_iA, "default"); - gAnimationManager.addAnimation(b, m_iB, "default"); - gAnimationManager.addAnimation({}, m_iC, "default"); + gAnimationManager.createAnimation(a, m_iA, "default"); + gAnimationManager.createAnimation(b, m_iB, "default"); + gAnimationManager.createAnimation({}, m_iC, "default"); } - CAnimatedVariable m_iA; - CAnimatedVariable m_iB; - CAnimatedVariable m_iC; + PANIMVAR m_iA; + PANIMVAR m_iB; + PANIMVAR m_iC; }; int main(int argc, char** argv, char** envp) { - INITANIMCFG("global"); - CREATEANIMCFG("default", "global"); - animationConfig["default"].internalBezier = "default"; - animationConfig["default"].internalSpeed = 1.0; - animationConfig["default"].internalEnabled = 1; - animationConfig["default"].pValues = &animationConfig["default"]; + animationConfig["default"] = makeShared(); + animationConfig["default"]->internalBezier = "default"; + animationConfig["default"]->internalSpeed = 1.0; + animationConfig["default"]->internalEnabled = 1; + animationConfig["default"]->pValues = animationConfig["default"]; int ret = 0; Subject s(0, 0); - EXPECT(s.m_iA.value(), 0); - EXPECT(s.m_iB.value(), 0); + EXPECT(s.m_iA->value(), 0); + EXPECT(s.m_iB->value(), 0); // Test destruction of a CAnimatedVariable { Subject s2(10, 10); // Adds them to active - s2.m_iA = 1; - s2.m_iB = 2; + *s2.m_iA = 1; + *s2.m_iB = 2; // We deliberately do not tick here, to make sure the destructor removes active animated variables } EXPECT(gAnimationManager.shouldTickForNext(), false); - EXPECT(s.m_iC.value().done, false); + EXPECT(s.m_iC->value().done, false); - s.m_iA = 10; - s.m_iB = 100; - s.m_iC = SomeTestType(true); + *s.m_iA = 10; + *s.m_iB = 100; + *s.m_iC = SomeTestType(true); - EXPECT(s.m_iC.value().done, false); + EXPECT(s.m_iC->value().done, false); while (gAnimationManager.shouldTickForNext()) { gAnimationManager.tick(); } - EXPECT(s.m_iA.value(), 10); - EXPECT(s.m_iB.value(), 100); - EXPECT(s.m_iC.value().done, true); + EXPECT(s.m_iA->value(), 10); + EXPECT(s.m_iB->value(), 100); + EXPECT(s.m_iC->value().done, true); - s.m_iA.setValue(0); - s.m_iB.setValue(0); + s.m_iA->setValue(0); + s.m_iB->setValue(0); while (gAnimationManager.shouldTickForNext()) { gAnimationManager.tick(); } - EXPECT(s.m_iA.value(), 10); - EXPECT(s.m_iB.value(), 100); + EXPECT(s.m_iA->value(), 10); + EXPECT(s.m_iB->value(), 100); // // Test callbacks @@ -161,11 +175,11 @@ int main(int argc, char** argv, char** envp) { bool beginCallbackRan = false; bool updateCallbackRan = false; bool endCallbackRan = false; - s.m_iA.setCallbackOnBegin([&beginCallbackRan](CBaseAnimatedVariable* av) { beginCallbackRan = true; }); - s.m_iA.setUpdateCallback([&updateCallbackRan](CBaseAnimatedVariable* av) { updateCallbackRan = true; }); - s.m_iA.setCallbackOnEnd([&endCallbackRan](CBaseAnimatedVariable* av) { endCallbackRan = true; }, false); + s.m_iA->setCallbackOnBegin([&beginCallbackRan](WP pav) { beginCallbackRan = true; }); + s.m_iA->setUpdateCallback([&updateCallbackRan](WP pav) { updateCallbackRan = true; }); + s.m_iA->setCallbackOnEnd([&endCallbackRan](WP pav) { endCallbackRan = true; }, false); - s.m_iA.setValueAndWarp(42); + s.m_iA->setValueAndWarp(42); EXPECT(beginCallbackRan, false); EXPECT(updateCallbackRan, true); @@ -175,7 +189,7 @@ int main(int argc, char** argv, char** envp) { updateCallbackRan = false; endCallbackRan = false; - s.m_iA = 1337; + *s.m_iA = 1337; while (gAnimationManager.shouldTickForNext()) { gAnimationManager.tick(); }