From 6a26d08bacb4d4883d8097328e26ce7dc5cc4215 Mon Sep 17 00:00:00 2001 From: Maximilian Seidler <78690852+PaideiaDilemma@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:02:21 +0000 Subject: [PATCH] animation: add CAnimationConfigTree (#29) Co-authored-by: Maximilian Seidler --- .../hyprutils/animation/AnimatedVariable.hpp | 17 +--- .../hyprutils/animation/AnimationConfig.hpp | 56 +++++++++++ src/animation/AnimationConfig.cpp | 70 ++++++++++++++ tests/animation.cpp | 94 +++++++++++++++---- 4 files changed, 205 insertions(+), 32 deletions(-) create mode 100644 include/hyprutils/animation/AnimationConfig.hpp create mode 100644 src/animation/AnimationConfig.cpp diff --git a/include/hyprutils/animation/AnimatedVariable.hpp b/include/hyprutils/animation/AnimatedVariable.hpp index 6b44b1a..0d57529 100644 --- a/include/hyprutils/animation/AnimatedVariable.hpp +++ b/include/hyprutils/animation/AnimatedVariable.hpp @@ -1,5 +1,6 @@ #pragma once +#include "AnimationConfig.hpp" #include "../memory/WeakPtr.hpp" #include "hyprutils/memory/SharedPtr.hpp" @@ -10,22 +11,6 @@ namespace Hyprutils { namespace Animation { class CAnimationManager; - /* - Structure for animation properties. - Config properties need to have a static lifetime to allow for config reload. - */ - struct SAnimationPropertyConfig { - bool overridden = true; - - std::string internalBezier = ""; - std::string internalStyle = ""; - float internalSpeed = 0.f; - int internalEnabled = -1; - - Memory::CWeakPointer pValues; - Memory::CWeakPointer pParentAnimation; - }; - /* A base class for animated variables. */ class CBaseAnimatedVariable { public: diff --git a/include/hyprutils/animation/AnimationConfig.hpp b/include/hyprutils/animation/AnimationConfig.hpp new file mode 100644 index 0000000..0741d5c --- /dev/null +++ b/include/hyprutils/animation/AnimationConfig.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "../memory/WeakPtr.hpp" +#include "hyprutils/memory/WeakPtr.hpp" + +#include + +namespace Hyprutils { + namespace Animation { + /* + Structure for animation properties. + Config properties need to have a static lifetime to allow for config reload. + */ + struct SAnimationPropertyConfig { + bool overridden = false; + + std::string internalBezier = ""; + std::string internalStyle = ""; + float internalSpeed = 0.f; + int internalEnabled = -1; + + Memory::CWeakPointer pValues; + Memory::CWeakPointer pParentAnimation; + }; + + /* A class to manage SAnimationPropertyConfig objects in a tree structure */ + class CAnimationConfigTree { + public: + CAnimationConfigTree() = default; + ~CAnimationConfigTree() = default; + + /* Add a new animation node inheriting from a parent. + If parent is empty, a root node will be created that references it's own values. + Make sure the parent node has already been created through this interface. */ + void createNode(const std::string& nodeName, const std::string& parent = ""); + + /* check if a node name has been created using createNode */ + bool nodeExists(const std::string& nodeName) const; + + /* Override the values of a node. The root node can also be overriden. */ + void setConfigForNode(const std::string& nodeName, int enabled, float speed, const std::string& bezier, const std::string& style = ""); + + Memory::CSharedPointer getConfig(const std::string& name) const; + const std::unordered_map>& getFullConfig() const; + + CAnimationConfigTree(const CAnimationConfigTree&) = delete; + CAnimationConfigTree(CAnimationConfigTree&&) = delete; + CAnimationConfigTree& operator=(const CAnimationConfigTree&) = delete; + CAnimationConfigTree& operator=(CAnimationConfigTree&&) = delete; + + private: + void setAnimForChildren(Memory::CSharedPointer PANIM); + std::unordered_map> m_mAnimationConfig; + }; + } +} diff --git a/src/animation/AnimationConfig.cpp b/src/animation/AnimationConfig.cpp new file mode 100644 index 0000000..e158bca --- /dev/null +++ b/src/animation/AnimationConfig.cpp @@ -0,0 +1,70 @@ +#include + +using namespace Hyprutils::Animation; +using namespace Hyprutils::Memory; + +#define SP CSharedPointer +#define WP CWeakPointer + +void CAnimationConfigTree::createNode(const std::string& nodeName, const std::string& parent) { + auto pConfig = m_mAnimationConfig[nodeName]; + if (!pConfig) + pConfig = makeShared(); + + WP parentRef; + if (!parent.empty() && m_mAnimationConfig.find(parent) != m_mAnimationConfig.end()) + parentRef = m_mAnimationConfig[parent]; + + *pConfig = { + .overridden = false, + .internalBezier = "", + .internalStyle = "", + .internalSpeed = 0.f, + .internalEnabled = -1, + .pValues = (parentRef) ? parentRef->pValues : pConfig, + .pParentAnimation = (parentRef) ? parentRef : pConfig, + }; + + m_mAnimationConfig[nodeName] = pConfig; +} + +bool CAnimationConfigTree::nodeExists(const std::string& nodeName) const { + return m_mAnimationConfig.find(nodeName) != m_mAnimationConfig.end(); +} + +void CAnimationConfigTree::setConfigForNode(const std::string& nodeName, int enabled, float speed, const std::string& bezier, const std::string& style) { + auto pConfig = m_mAnimationConfig[nodeName]; + if (!pConfig) + return; + + *pConfig = { + .overridden = true, + .internalBezier = bezier, + .internalStyle = style, + .internalSpeed = speed, + .internalEnabled = enabled, + .pValues = pConfig, + .pParentAnimation = pConfig->pParentAnimation, // keep the parent! + }; + + setAnimForChildren(pConfig); +} + +SP CAnimationConfigTree::getConfig(const std::string& name) const { + return m_mAnimationConfig.at(name); +} + +const std::unordered_map>& CAnimationConfigTree::getFullConfig() const { + return m_mAnimationConfig; +} + +void CAnimationConfigTree::setAnimForChildren(SP PANIM) { + for (auto& [name, anim] : m_mAnimationConfig) { + if (anim->pParentAnimation == PANIM && !anim->overridden) { + // if a child isnt overridden, set the values of the parent + anim->pValues = PANIM->pValues; + + setAnimForChildren(anim); + } + } +} diff --git a/tests/animation.cpp b/tests/animation.cpp index 04f50d3..f1cea31 100644 --- a/tests/animation.cpp +++ b/tests/animation.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -37,7 +38,7 @@ struct SomeTestType { } }; -std::unordered_map> animationConfig; +CAnimationConfigTree animationTree; class CMyAnimationManager : public CAnimationManager { public: @@ -90,7 +91,7 @@ class CMyAnimationManager : public CAnimationManager { const auto PAV = makeShared>(); PAV->create(EAVTYPE, static_cast(this), PAV, v); - PAV->setConfig(animationConfig[animationConfigName]); + PAV->setConfig(animationTree.getConfig(animationConfigName)); av = std::move(PAV); } @@ -109,7 +110,7 @@ class Subject { public: Subject(const int& a, const int& b) { gAnimationManager.createAnimation(a, m_iA, "default"); - gAnimationManager.createAnimation(b, m_iB, "default"); + gAnimationManager.createAnimation(b, m_iB, "internal"); gAnimationManager.createAnimation({}, m_iC, "default"); } PANIMVAR m_iA; @@ -117,15 +118,78 @@ class Subject { PANIMVAR 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) { - animationConfig["default"] = makeShared(); - animationConfig["default"]->internalBezier = "default"; - animationConfig["default"]->internalSpeed = 1.0; - animationConfig["default"]->internalStyle = "asdf"; - animationConfig["default"]->internalEnabled = 1; - animationConfig["default"]->pValues = animationConfig["default"]; - - int ret = 0; + 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); @@ -172,7 +236,7 @@ int main(int argc, char** argv, char** envp) { EXPECT(s.m_iA->getStyle(), "asdf"); EXPECT(s.m_iA->enabled(), true); - animationConfig["default"]->internalEnabled = 0; + animationTree.getConfig("global")->internalEnabled = 0; EXPECT(s.m_iA->enabled(), false); @@ -181,16 +245,14 @@ int main(int argc, char** argv, char** envp) { EXPECT(s.m_iA->value(), 50); // Test missing pValues - animationConfig["default"]->internalEnabled = 1; - animationConfig["default"]->pValues.reset(); + 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); - animationConfig["default"]->pValues = animationConfig["default"]; - // // Test callbacks //