#include <hyprutils/animation/AnimationConfig.hpp> #include <hyprutils/animation/AnimationManager.hpp> #include <hyprutils/animation/AnimatedVariable.hpp> #include <hyprutils/memory/WeakPtr.hpp> #include "shared.hpp" #define SP CSharedPointer #define WP CWeakPointer using namespace Hyprutils::Animation; using namespace Hyprutils::Math; using namespace Hyprutils::Memory; class EmtpyContext {}; template <typename VarType> using CAnimatedVariable = CGenericAnimatedVariable<VarType, EmtpyContext>; template <typename VarType> using PANIMVAR = SP<CAnimatedVariable<VarType>>; template <typename VarType> using PANIMVARREF = WP<CAnimatedVariable<VarType>>; enum eAVTypes { INT = 1, TEST, }; struct SomeTestType { bool done = false; bool operator==(const SomeTestType& other) const { return done == other.done; } SomeTestType& operator=(const SomeTestType& other) { done = other.done; return *this; } }; CAnimationConfigTree animationTree; class CMyAnimationManager : public CAnimationManager { public: void tick() { for (auto const& av : m_vActiveAnimatedVariables) { const auto PAV = av.lock(); if (!PAV || !PAV->ok()) continue; const auto SPENT = PAV->getPercent(); const auto PBEZIER = getBezier(PAV->getBezierName()); const auto POINTY = PBEZIER->getYForPoint(SPENT); if (POINTY >= 1.f || !PAV->enabled()) { PAV->warp(); continue; } switch (PAV->m_Type) { case eAVTypes::INT: { auto avInt = dynamic_cast<CAnimatedVariable<int>*>(PAV.get()); if (!avInt) std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET; const auto DELTA = avInt->goal() - avInt->value(); avInt->value() = avInt->begun() + (DELTA * POINTY); } break; case eAVTypes::TEST: { auto avCustom = dynamic_cast<CAnimatedVariable<SomeTestType>*>(PAV.get()); if (!avCustom) std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET; if (SPENT >= 1.f) avCustom->value().done = true; } break; default: { std::cout << Colors::RED << "What are we even doing?" << Colors::RESET; } break; } av->onUpdate(); } tickDone(); } template <typename VarType> void createAnimation(const VarType& v, PANIMVAR<VarType>& av, const std::string& animationConfigName) { constexpr const eAVTypes EAVTYPE = std::is_same_v<VarType, int> ? eAVTypes::INT : eAVTypes::TEST; const auto PAV = makeShared<CGenericAnimatedVariable<VarType, EmtpyContext>>(); PAV->create(EAVTYPE, static_cast<CAnimationManager*>(this), PAV, v); PAV->setConfig(animationTree.getConfig(animationConfigName)); av = std::move(PAV); } virtual void scheduleTick() { ; } virtual void onTicked() { ; } }; CMyAnimationManager gAnimationManager; class Subject { public: Subject(const int& a, const int& b) { gAnimationManager.createAnimation(a, m_iA, "default"); gAnimationManager.createAnimation(b, m_iB, "internal"); gAnimationManager.createAnimation({}, m_iC, "default"); } PANIMVAR<int> m_iA; PANIMVAR<int> m_iB; PANIMVAR<SomeTestType> m_iC; }; int config() { int ret = 0; animationTree.createNode("global"); animationTree.createNode("internal"); animationTree.createNode("foo", "internal"); animationTree.createNode("default", "global"); animationTree.createNode("bar", "default"); /* internal ↳ foo global ↳ default ↳ bar */ auto barCfg = animationTree.getConfig("bar"); auto internalCfg = animationTree.getConfig("internal"); // internal is a root node and should point to itself EXPECT(internalCfg->pParentAnimation.get(), internalCfg.get()); EXPECT(internalCfg->pValues.get(), internalCfg.get()); animationTree.setConfigForNode("global", 1, 4.0, "default", "asdf"); EXPECT(barCfg->internalEnabled, -1); { const auto PVALUES = barCfg->pValues.lock(); EXPECT(PVALUES->internalEnabled, 1); EXPECT(PVALUES->internalBezier, "default"); EXPECT(PVALUES->internalStyle, "asdf"); EXPECT(PVALUES->internalSpeed, 4.0); } EXPECT(barCfg->pParentAnimation.get(), animationTree.getConfig("default").get()); // Overwrite our own values animationTree.setConfigForNode("bar", 1, 4.2, "test", "qwer"); { const auto PVALUES = barCfg->pValues.lock(); EXPECT(PVALUES->internalEnabled, 1); EXPECT(PVALUES->internalBezier, "test"); EXPECT(PVALUES->internalStyle, "qwer"); EXPECT(PVALUES->internalSpeed, 4.2f); } // Now overwrite the parent animationTree.setConfigForNode("default", 0, 0.0, "zxcv", "foo"); { // Expecting no change const auto PVALUES = barCfg->pValues.lock(); EXPECT(PVALUES->internalEnabled, 1); EXPECT(PVALUES->internalBezier, "test"); EXPECT(PVALUES->internalStyle, "qwer"); EXPECT(PVALUES->internalSpeed, 4.2f); } return ret; } int main(int argc, char** argv, char** envp) { int ret = config(); animationTree.createNode("global"); animationTree.createNode("internal"); animationTree.createNode("default", "global"); animationTree.setConfigForNode("global", 1, 4.0, "default", "asdf"); Subject s(0, 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; // 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); *s.m_iA = 10; *s.m_iB = 100; *s.m_iC = SomeTestType(true); 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); 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); // Test config stuff EXPECT(s.m_iA->getBezierName(), "default"); EXPECT(s.m_iA->getStyle(), "asdf"); EXPECT(s.m_iA->enabled(), true); animationTree.getConfig("global")->internalEnabled = 0; EXPECT(s.m_iA->enabled(), false); *s.m_iA = 50; gAnimationManager.tick(); // Expecting a warp EXPECT(s.m_iA->value(), 50); // Test missing pValues animationTree.getConfig("global")->internalEnabled = 0; animationTree.getConfig("default")->pValues.reset(); EXPECT(s.m_iA->enabled(), false); EXPECT(s.m_iA->getBezierName(), "default"); EXPECT(s.m_iA->getStyle(), ""); EXPECT(s.m_iA->getPercent(), 1.f); // // Test callbacks // bool beginCallbackRan = false; bool updateCallbackRan = false; bool endCallbackRan = false; s.m_iA->setCallbackOnBegin([&beginCallbackRan](WP<CBaseAnimatedVariable> pav) { beginCallbackRan = true; }); s.m_iA->setUpdateCallback([&updateCallbackRan](WP<CBaseAnimatedVariable> pav) { updateCallbackRan = true; }); s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> pav) { endCallbackRan = true; }, false); s.m_iA->setValueAndWarp(42); EXPECT(beginCallbackRan, false); EXPECT(updateCallbackRan, true); EXPECT(endCallbackRan, true); beginCallbackRan = false; updateCallbackRan = false; endCallbackRan = false; *s.m_iA = 1337; while (gAnimationManager.shouldTickForNext()) { gAnimationManager.tick(); } EXPECT(beginCallbackRan, true); EXPECT(updateCallbackRan, true); EXPECT(endCallbackRan, true); return ret; }