From 180120801a66d24f5064536824e80c58a7f4970f Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Sat, 11 Apr 2026 21:33:18 +1000 Subject: [PATCH] feat: add proper anim tokens to Tokens --- plugin/src/Caelestia/Config/CMakeLists.txt | 1 + plugin/src/Caelestia/Config/anim.cpp | 62 +++++++++++++++++ plugin/src/Caelestia/Config/anim.hpp | 78 ++++++++++++++++++++++ plugin/src/Caelestia/Config/tokens.cpp | 46 ++++++++----- plugin/src/Caelestia/Config/tokens.hpp | 9 ++- 5 files changed, 177 insertions(+), 19 deletions(-) create mode 100644 plugin/src/Caelestia/Config/anim.cpp create mode 100644 plugin/src/Caelestia/Config/anim.hpp diff --git a/plugin/src/Caelestia/Config/CMakeLists.txt b/plugin/src/Caelestia/Config/CMakeLists.txt index 9d8a9d99..92ee2da9 100644 --- a/plugin/src/Caelestia/Config/CMakeLists.txt +++ b/plugin/src/Caelestia/Config/CMakeLists.txt @@ -6,6 +6,7 @@ qml_module(caelestia-config configscope.cpp appearanceconfig.cpp tokens.cpp + anim.cpp monitorconfigmanager.cpp backgroundconfig.hpp barconfig.hpp diff --git a/plugin/src/Caelestia/Config/anim.cpp b/plugin/src/Caelestia/Config/anim.cpp new file mode 100644 index 00000000..9c2b9111 --- /dev/null +++ b/plugin/src/Caelestia/Config/anim.cpp @@ -0,0 +1,62 @@ +#include "anim.hpp" +#include "appearanceconfig.hpp" +#include "tokens.hpp" + +#include + +namespace caelestia::config { + +AnimTokens::AnimTokens(QObject* parent) + : QObject(parent) {} + +QEasingCurve AnimTokens::buildCurve(const QList& points) { + QEasingCurve curve(QEasingCurve::BezierSpline); + + // Points come in pairs of (x, y) forming cubic bezier segments. + // Each segment needs 3 control points: c1, c2, endPoint. + // So 6 values per segment: c1x, c1y, c2x, c2y, endX, endY. + for (int i = 0; i + 5 < points.size(); i += 6) { + QPointF c1(points[i], points[i + 1]); + QPointF c2(points[i + 2], points[i + 3]); + QPointF end(points[i + 4], points[i + 5]); + curve.addCubicBezierSegment(c1, c2, end); + } + + return curve; +} + +void AnimTokens::rebuildCurves() { + if (!m_curves) + return; + + m_emphasized = buildCurve(m_curves->emphasized()); + m_emphasizedAccel = buildCurve(m_curves->emphasizedAccel()); + m_emphasizedDecel = buildCurve(m_curves->emphasizedDecel()); + m_standard = buildCurve(m_curves->standard()); + m_standardAccel = buildCurve(m_curves->standardAccel()); + m_standardDecel = buildCurve(m_curves->standardDecel()); + m_expressiveFastSpatial = buildCurve(m_curves->expressiveFastSpatial()); + m_expressiveDefaultSpatial = buildCurve(m_curves->expressiveDefaultSpatial()); + m_expressiveSlowSpatial = buildCurve(m_curves->expressiveSlowSpatial()); + + emit curvesChanged(); +} + +void AnimTokens::bindCurves(AnimCurves* curves) { + m_curves = curves; + + // Rebuild when any curve control points change + connect(curves, &AnimCurves::propertiesChanged, this, &AnimTokens::rebuildCurves); + + rebuildCurves(); +} + +void AnimTokens::bindDurations(AnimDurations* durations) { + if (m_durations == durations) + return; + + m_durations = durations; + emit durationsChanged(); +} + +} // namespace caelestia::config diff --git a/plugin/src/Caelestia/Config/anim.hpp b/plugin/src/Caelestia/Config/anim.hpp new file mode 100644 index 00000000..464ee32a --- /dev/null +++ b/plugin/src/Caelestia/Config/anim.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include + +namespace caelestia::config { + +class AnimCurves; +class AnimDurations; +class ConfigScope; + +class AnimTokens : public QObject { + Q_OBJECT + Q_MOC_INCLUDE("appearanceconfig.hpp") + QML_ANONYMOUS + + Q_PROPERTY(QEasingCurve emphasized READ emphasized NOTIFY curvesChanged) + Q_PROPERTY(QEasingCurve emphasizedAccel READ emphasizedAccel NOTIFY curvesChanged) + Q_PROPERTY(QEasingCurve emphasizedDecel READ emphasizedDecel NOTIFY curvesChanged) + Q_PROPERTY(QEasingCurve standard READ standard NOTIFY curvesChanged) + Q_PROPERTY(QEasingCurve standardAccel READ standardAccel NOTIFY curvesChanged) + Q_PROPERTY(QEasingCurve standardDecel READ standardDecel NOTIFY curvesChanged) + Q_PROPERTY(QEasingCurve expressiveFastSpatial READ expressiveFastSpatial NOTIFY curvesChanged) + Q_PROPERTY(QEasingCurve expressiveDefaultSpatial READ expressiveDefaultSpatial NOTIFY curvesChanged) + Q_PROPERTY(QEasingCurve expressiveSlowSpatial READ expressiveSlowSpatial NOTIFY curvesChanged) + + Q_PROPERTY(caelestia::config::AnimDurations* durations READ durations NOTIFY durationsChanged) + +public: + explicit AnimTokens(QObject* parent = nullptr); + + void bindCurves(AnimCurves* curves); + void bindDurations(AnimDurations* durations); + + [[nodiscard]] QEasingCurve emphasized() const { return m_emphasized; } + + [[nodiscard]] QEasingCurve emphasizedAccel() const { return m_emphasizedAccel; } + + [[nodiscard]] QEasingCurve emphasizedDecel() const { return m_emphasizedDecel; } + + [[nodiscard]] QEasingCurve standard() const { return m_standard; } + + [[nodiscard]] QEasingCurve standardAccel() const { return m_standardAccel; } + + [[nodiscard]] QEasingCurve standardDecel() const { return m_standardDecel; } + + [[nodiscard]] QEasingCurve expressiveFastSpatial() const { return m_expressiveFastSpatial; } + + [[nodiscard]] QEasingCurve expressiveDefaultSpatial() const { return m_expressiveDefaultSpatial; } + + [[nodiscard]] QEasingCurve expressiveSlowSpatial() const { return m_expressiveSlowSpatial; } + + [[nodiscard]] AnimDurations* durations() const { return m_durations; } + +signals: + void curvesChanged(); + void durationsChanged(); + +private: + void rebuildCurves(); + static QEasingCurve buildCurve(const QList& points); + + AnimCurves* m_curves = nullptr; + AnimDurations* m_durations = nullptr; + + QEasingCurve m_emphasized; + QEasingCurve m_emphasizedAccel; + QEasingCurve m_emphasizedDecel; + QEasingCurve m_standard; + QEasingCurve m_standardAccel; + QEasingCurve m_standardDecel; + QEasingCurve m_expressiveFastSpatial; + QEasingCurve m_expressiveDefaultSpatial; + QEasingCurve m_expressiveSlowSpatial; +}; + +} // namespace caelestia::config diff --git a/plugin/src/Caelestia/Config/tokens.cpp b/plugin/src/Caelestia/Config/tokens.cpp index 281d232f..bf808249 100644 --- a/plugin/src/Caelestia/Config/tokens.cpp +++ b/plugin/src/Caelestia/Config/tokens.cpp @@ -62,18 +62,6 @@ TokenConfig* TokenConfig::create(QQmlEngine* engine, QJSEngine*) { // Tokens (attached type) -Tokens::Tokens(ConfigScope* scope, QObject* parent) - : QObject(parent) - , m_scope(scope) { - connectScope(); -} - -void Tokens::connectScope() { - if (!m_scope) - return; - connect(m_scope, &ConfigScope::configChanged, this, &Tokens::sourceChanged); -} - // Resolve appearance from per-monitor GlobalConfig overlay or global GlobalConfig static const AppearanceConfig* resolveAppearance(ConfigScope* scope) { if (scope && scope->config()) @@ -82,6 +70,35 @@ static const AppearanceConfig* resolveAppearance(ConfigScope* scope) { return global ? global->appearance() : nullptr; } +Tokens::Tokens(ConfigScope* scope, QObject* parent) + : QObject(parent) + , m_scope(scope) + , m_anim(new AnimTokens(this)) { + connectScope(); + bindAnim(); +} + +void Tokens::connectScope() { + if (!m_scope) + return; + connect(m_scope, &ConfigScope::configChanged, this, &Tokens::sourceChanged); + connect(m_scope, &ConfigScope::configChanged, this, &Tokens::bindAnim); +} + +void Tokens::bindAnim() { + auto* appearance = resolveAppearance(m_scope); + if (!appearance) + return; + + // Bind durations from resolved GlobalConfig appearance + m_anim->bindDurations(appearance->anim()->durations()); + + // Bind curves from TokenConfig + auto* tokens = TokenConfig::instance(); + if (tokens) + m_anim->bindCurves(tokens->appearance()->curves()); +} + const AppearanceRounding* Tokens::rounding() const { auto* a = resolveAppearance(m_scope); return a ? a->rounding() : nullptr; @@ -102,11 +119,6 @@ const AppearanceFont* Tokens::font() const { return a ? a->font() : nullptr; } -const AppearanceAnim* Tokens::anim() const { - auto* a = resolveAppearance(m_scope); - return a ? a->anim() : nullptr; -} - const AppearanceTransparency* Tokens::transparency() const { auto* a = resolveAppearance(m_scope); return a ? a->transparency() : nullptr; diff --git a/plugin/src/Caelestia/Config/tokens.hpp b/plugin/src/Caelestia/Config/tokens.hpp index 27858ccb..a9d879b4 100644 --- a/plugin/src/Caelestia/Config/tokens.hpp +++ b/plugin/src/Caelestia/Config/tokens.hpp @@ -1,5 +1,6 @@ #pragma once +#include "anim.hpp" #include "appearanceconfig.hpp" #include "configobject.hpp" @@ -356,9 +357,9 @@ class Tokens : public QObject { Q_PROPERTY(const caelestia::config::AppearanceSpacing* spacing READ spacing NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::AppearancePadding* padding READ padding NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::AppearanceFont* font READ font NOTIFY sourceChanged) - Q_PROPERTY(const caelestia::config::AppearanceAnim* anim READ anim NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::AppearanceTransparency* transparency READ transparency NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::SizeTokens* sizes READ sizes NOTIFY sourceChanged) + Q_PROPERTY(const caelestia::config::AnimTokens* anim READ anim NOTIFY sourceChanged) public: explicit Tokens(ConfigScope* scope, QObject* parent = nullptr); @@ -367,10 +368,12 @@ public: [[nodiscard]] const AppearanceSpacing* spacing() const; [[nodiscard]] const AppearancePadding* padding() const; [[nodiscard]] const AppearanceFont* font() const; - [[nodiscard]] const AppearanceAnim* anim() const; [[nodiscard]] const AppearanceTransparency* transparency() const; + [[nodiscard]] const SizeTokens* sizes() const; + [[nodiscard]] const AnimTokens* anim() const { return m_anim; } + static Tokens* qmlAttachedProperties(QObject* object); signals: @@ -378,8 +381,10 @@ signals: private: void connectScope(); + void bindAnim(); ConfigScope* m_scope; + AnimTokens* m_anim = nullptr; }; } // namespace caelestia::config