From f2575b3331607548d14d4fb7823c525921ad772d Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Mon, 20 Apr 2026 23:35:34 +1000 Subject: [PATCH] feat: add role-based font tokens --- plugin/src/Caelestia/Config/CMakeLists.txt | 1 + .../src/Caelestia/Config/appearanceconfig.cpp | 31 --- .../src/Caelestia/Config/appearanceconfig.hpp | 74 +++--- plugin/src/Caelestia/Config/config.cpp | 1 - plugin/src/Caelestia/Config/font.cpp | 218 ++++++++++++++++++ plugin/src/Caelestia/Config/font.hpp | 147 ++++++++++++ plugin/src/Caelestia/Config/fontbuilder.cpp | 3 - .../src/Caelestia/Config/tokensattached.cpp | 14 +- .../src/Caelestia/Config/tokensattached.hpp | 9 +- 9 files changed, 425 insertions(+), 73 deletions(-) create mode 100644 plugin/src/Caelestia/Config/font.cpp create mode 100644 plugin/src/Caelestia/Config/font.hpp diff --git a/plugin/src/Caelestia/Config/CMakeLists.txt b/plugin/src/Caelestia/Config/CMakeLists.txt index 05008965..b6f4ef08 100644 --- a/plugin/src/Caelestia/Config/CMakeLists.txt +++ b/plugin/src/Caelestia/Config/CMakeLists.txt @@ -6,6 +6,7 @@ qml_module(caelestia-config configobject.cpp rootconfig.cpp appearanceconfig.cpp + font.cpp fontbuilder.cpp tokens.cpp tokensattached.cpp diff --git a/plugin/src/Caelestia/Config/appearanceconfig.cpp b/plugin/src/Caelestia/Config/appearanceconfig.cpp index eaf98b8e..5a89baf0 100644 --- a/plugin/src/Caelestia/Config/appearanceconfig.cpp +++ b/plugin/src/Caelestia/Config/appearanceconfig.cpp @@ -142,37 +142,6 @@ int AppearancePadding::extraExtraLarge() const { return m_tokens ? static_cast(m_tokens->extraExtraLarge() * m_scale) : 0; } -// FontSize - -void FontSize::bindTokens(FontSizeTokens* tokens) { - m_tokens = tokens; - connectTokenSignals(tokens, this); -} - -int FontSize::small() const { - return m_tokens ? static_cast(m_tokens->small() * m_scale) : 0; -} - -int FontSize::smaller() const { - return m_tokens ? static_cast(m_tokens->smaller() * m_scale) : 0; -} - -int FontSize::normal() const { - return m_tokens ? static_cast(m_tokens->normal() * m_scale) : 0; -} - -int FontSize::larger() const { - return m_tokens ? static_cast(m_tokens->larger() * m_scale) : 0; -} - -int FontSize::large() const { - return m_tokens ? static_cast(m_tokens->large() * m_scale) : 0; -} - -int FontSize::extraLarge() const { - return m_tokens ? static_cast(m_tokens->extraLarge() * m_scale) : 0; -} - // AnimDurations void AnimDurations::bindTokens(AnimDurationTokens* tokens) { diff --git a/plugin/src/Caelestia/Config/appearanceconfig.hpp b/plugin/src/Caelestia/Config/appearanceconfig.hpp index 23008f15..2097db52 100644 --- a/plugin/src/Caelestia/Config/appearanceconfig.hpp +++ b/plugin/src/Caelestia/Config/appearanceconfig.hpp @@ -2,7 +2,9 @@ #include "configobject.hpp" +#include #include +#include namespace caelestia::config { @@ -10,7 +12,6 @@ namespace caelestia::config { class RoundingTokens; class SpacingTokens; class PaddingTokens; -class FontSizeTokens; class AnimDurationTokens; class AppearanceRounding : public ConfigObject { @@ -126,65 +127,70 @@ private: PaddingTokens* m_tokens = nullptr; }; -class FontFamily : public ConfigObject { +class FontConfig : public ConfigObject { Q_OBJECT QML_ANONYMOUS - CONFIG_PROPERTY(QString, sans, QStringLiteral("Rubik")) - CONFIG_PROPERTY(QString, mono, QStringLiteral("CaskaydiaCove NF")) - CONFIG_PROPERTY(QString, material, QStringLiteral("Material Symbols Rounded")) - CONFIG_PROPERTY(QString, clock, QStringLiteral("Rubik")) + CONFIG_PROPERTY(QString, family, QStringLiteral("Rubik")) + CONFIG_PROPERTY(int, size, 14) + CONFIG_PROPERTY(int, weight, QFont::Normal) + CONFIG_PROPERTY(bool, italic, false) + CONFIG_PROPERTY(QVariantMap, vaxes, {}) public: - explicit FontFamily(QObject* parent = nullptr) + explicit FontConfig(QObject* parent = nullptr) : ConfigObject(parent) {} }; -class FontSize : public ConfigObject { +class FontStyleConfig : public ConfigObject { Q_OBJECT QML_ANONYMOUS - CONFIG_PROPERTY(qreal, scale, 1) - - Q_PROPERTY(int small READ small NOTIFY valuesChanged) - Q_PROPERTY(int smaller READ smaller NOTIFY valuesChanged) - Q_PROPERTY(int normal READ normal NOTIFY valuesChanged) - Q_PROPERTY(int larger READ larger NOTIFY valuesChanged) - Q_PROPERTY(int large READ large NOTIFY valuesChanged) - Q_PROPERTY(int extraLarge READ extraLarge NOTIFY valuesChanged) + CONFIG_SUBOBJECT(FontConfig, large) + CONFIG_SUBOBJECT(FontConfig, medium) + CONFIG_SUBOBJECT(FontConfig, small) public: - explicit FontSize(QObject* parent = nullptr) - : ConfigObject(parent) {} + explicit FontStyleConfig(QObject* parent = nullptr) + : ConfigObject(parent) + , m_large(new FontConfig(this)) + , m_medium(new FontConfig(this)) + , m_small(new FontConfig(this)) {} +}; - void bindTokens(FontSizeTokens* tokens); +class IconFontStyleConfig : public FontStyleConfig { + Q_OBJECT + QML_ANONYMOUS - [[nodiscard]] int small() const; - [[nodiscard]] int smaller() const; - [[nodiscard]] int normal() const; - [[nodiscard]] int larger() const; - [[nodiscard]] int large() const; - [[nodiscard]] int extraLarge() const; + CONFIG_SUBOBJECT(FontConfig, extraLarge) -signals: - void valuesChanged(); - -private: - FontSizeTokens* m_tokens = nullptr; +public: + explicit IconFontStyleConfig(QObject* parent = nullptr) + : FontStyleConfig(parent) + , m_extraLarge(new FontConfig(this)) {} }; class AppearanceFont : public ConfigObject { Q_OBJECT QML_ANONYMOUS - CONFIG_SUBOBJECT(FontFamily, family) - CONFIG_SUBOBJECT(FontSize, size) + CONFIG_SUBOBJECT(FontStyleConfig, headline) + CONFIG_SUBOBJECT(FontStyleConfig, title) + CONFIG_SUBOBJECT(FontStyleConfig, body) + CONFIG_SUBOBJECT(FontStyleConfig, label) + CONFIG_SUBOBJECT(FontStyleConfig, mono) + CONFIG_SUBOBJECT(IconFontStyleConfig, icon) + CONFIG_PROPERTY(QString, clock, QStringLiteral("Rubik")) public: explicit AppearanceFont(QObject* parent = nullptr) : ConfigObject(parent) - , m_family(new FontFamily(this)) - , m_size(new FontSize(this)) {} + , m_headline(new FontStyleConfig(this)) + , m_title(new FontStyleConfig(this)) + , m_body(new FontStyleConfig(this)) + , m_label(new FontStyleConfig(this)) + , m_mono(new FontStyleConfig(this)) + , m_icon(new IconFontStyleConfig(this)) {} }; class AnimDurations : public ConfigObject { diff --git a/plugin/src/Caelestia/Config/config.cpp b/plugin/src/Caelestia/Config/config.cpp index ff939f99..ecd63bb5 100644 --- a/plugin/src/Caelestia/Config/config.cpp +++ b/plugin/src/Caelestia/Config/config.cpp @@ -103,7 +103,6 @@ void GlobalConfig::bindAppearanceTokens() { m_appearance->rounding()->bindTokens(tokenAppearance->rounding()); m_appearance->spacing()->bindTokens(tokenAppearance->spacing()); m_appearance->padding()->bindTokens(tokenAppearance->padding()); - m_appearance->font()->size()->bindTokens(tokenAppearance->fontSize()); m_appearance->anim()->durations()->bindTokens(tokenAppearance->animDurations()); m_tokensBound = true; } diff --git a/plugin/src/Caelestia/Config/font.cpp b/plugin/src/Caelestia/Config/font.cpp new file mode 100644 index 00000000..982b36d1 --- /dev/null +++ b/plugin/src/Caelestia/Config/font.cpp @@ -0,0 +1,218 @@ +#include "font.hpp" +#include "appearanceconfig.hpp" + +namespace caelestia::config { + +// FontStyleBase + +QFont FontStyleBase::large() const { + return m_large; +} + +QFont FontStyleBase::medium() const { + return m_medium; +} + +QFont FontStyleBase::small() const { + return m_small; +} + +QFont FontStyleBase::buildFont(const FontConfig* cfg) { + QFont font; + font.setFamily(cfg->family()); + font.setPointSize(cfg->size()); + font.setWeight(QFont::Weight(cfg->weight())); + font.setItalic(cfg->italic()); + + const auto axes = cfg->vaxes(); + for (auto it = axes.constBegin(); it != axes.constEnd(); ++it) { + if (auto tag = QFont::Tag::fromString(it.key())) + font.setVariableAxis(*tag, it.value().toFloat()); + } + + return font; +} + +void FontStyleBase::bind(FontStyleConfig* cfg) { + if (m_cfg == cfg) + return; + + if (m_cfg) { + disconnect(m_cfg->large(), nullptr, this, nullptr); + disconnect(m_cfg->medium(), nullptr, this, nullptr); + disconnect(m_cfg->small(), nullptr, this, nullptr); + } + + m_cfg = cfg; + + if (cfg) { + connect(cfg->large(), &ConfigObject::propertiesChanged, this, &FontStyleBase::rebuild); + connect(cfg->medium(), &ConfigObject::propertiesChanged, this, &FontStyleBase::rebuild); + connect(cfg->small(), &ConfigObject::propertiesChanged, this, &FontStyleBase::rebuild); + } + + rebuild(); +} + +void FontStyleBase::rebuild() { + if (m_cfg) { + m_large = buildFont(m_cfg->large()); + m_medium = buildFont(m_cfg->medium()); + m_small = buildFont(m_cfg->small()); + } else { + m_large = QFont(); + m_medium = QFont(); + m_small = QFont(); + } + emit fontsChanged(); +} + +// FontStyle + +FontStyle::FontStyle(QObject* parent) + : FontStyleBase(parent) + , m_builders(new FontBuilders(this, this)) {} + +FontBuilders* FontStyle::builders() const { + return m_builders; +} + +// IconFontStyle + +void IconFontStyle::bind(IconFontStyleConfig* cfg) { + if (m_cfg == cfg) + return; + + if (m_cfg) { + // Previous cfg may or may not be an IconFontStyleConfig; guard via cast. + if (auto* prev = qobject_cast(m_cfg)) + disconnect(prev->extraLarge(), nullptr, this, nullptr); + } + + FontStyleBase::bind(cfg); + + if (cfg) + connect(cfg->extraLarge(), &ConfigObject::propertiesChanged, this, &IconFontStyle::rebuild); +} + +QFont IconFontStyle::extraLarge() const { + return m_extraLarge; +} + +FontBuilder IconFontStyle::builder() const { + return FontBuilder(m_large); +} + +void IconFontStyle::rebuild() { + if (auto* cfg = qobject_cast(m_cfg)) { + m_large = buildFont(cfg->large()); + m_medium = buildFont(cfg->medium()); + m_small = buildFont(cfg->small()); + m_extraLarge = buildFont(cfg->extraLarge()); + } else { + m_large = QFont(); + m_medium = QFont(); + m_small = QFont(); + m_extraLarge = QFont(); + } + emit fontsChanged(); +} + +// FontBuilders + +FontBuilders::FontBuilders(const FontStyleBase* style, QObject* parent) + : QObject(parent) + , m_style(style) { + connect(style, &FontStyleBase::fontsChanged, this, &FontBuilders::buildersChanged); +} + +FontBuilder FontBuilders::large() const { + return FontBuilder(m_style->large()); +} + +FontBuilder FontBuilders::medium() const { + return FontBuilder(m_style->medium()); +} + +FontBuilder FontBuilders::small() const { + return FontBuilder(m_style->small()); +} + +// FontTokens + +FontTokens::FontTokens(QObject* parent) + : QObject(parent) + , m_headline(new FontStyle(this)) + , m_title(new FontStyle(this)) + , m_body(new FontStyle(this)) + , m_label(new FontStyle(this)) + , m_mono(new FontStyle(this)) + , m_icon(new IconFontStyle(this)) {} + +FontStyle* FontTokens::headline() const { + return m_headline; +} + +FontStyle* FontTokens::title() const { + return m_title; +} + +FontStyle* FontTokens::body() const { + return m_body; +} + +FontStyle* FontTokens::label() const { + return m_label; +} + +FontStyle* FontTokens::mono() const { + return m_mono; +} + +IconFontStyle* FontTokens::icon() const { + return m_icon; +} + +FontBuilder FontTokens::clock() const { + return FontBuilder(m_clock); +} + +void FontTokens::bindFont(AppearanceFont* font) { + if (m_font == font) + return; + + if (m_font) + disconnect(m_font, nullptr, this, nullptr); + + m_font = font; + + if (font) { + m_headline->bind(font->headline()); + m_title->bind(font->title()); + m_body->bind(font->body()); + m_label->bind(font->label()); + m_mono->bind(font->mono()); + m_icon->bind(font->icon()); + + connect(font, &AppearanceFont::clockChanged, this, &FontTokens::rebuildClock); + } else { + m_headline->bind(nullptr); + m_title->bind(nullptr); + m_body->bind(nullptr); + m_label->bind(nullptr); + m_mono->bind(nullptr); + m_icon->bind(nullptr); + } + + rebuildClock(); +} + +void FontTokens::rebuildClock() { + QFont f; + if (m_font) + f.setFamily(m_font->clock()); + m_clock = f; + emit clockChanged(); +} + +} // namespace caelestia::config diff --git a/plugin/src/Caelestia/Config/font.hpp b/plugin/src/Caelestia/Config/font.hpp new file mode 100644 index 00000000..ebc35e5f --- /dev/null +++ b/plugin/src/Caelestia/Config/font.hpp @@ -0,0 +1,147 @@ +#pragma once + +#include "fontbuilder.hpp" + +#include + +namespace caelestia::config { + +class AppearanceFont; +class FontConfig; +class FontStyleConfig; +class IconFontStyleConfig; +class FontStyleBase; + +class FontBuilders : public QObject { + Q_OBJECT + QML_ANONYMOUS + + Q_PROPERTY(caelestia::config::FontBuilder large READ large NOTIFY buildersChanged FINAL) + Q_PROPERTY(caelestia::config::FontBuilder medium READ medium NOTIFY buildersChanged FINAL) + Q_PROPERTY(caelestia::config::FontBuilder small READ small NOTIFY buildersChanged FINAL) + +public: + explicit FontBuilders(const FontStyleBase* style, QObject* parent = nullptr); + + [[nodiscard]] FontBuilder large() const; + [[nodiscard]] FontBuilder medium() const; + [[nodiscard]] FontBuilder small() const; + +signals: + void buildersChanged(); + +private: + const FontStyleBase* m_style; +}; + +class FontStyleBase : public QObject { + Q_OBJECT + + Q_PROPERTY(QFont large READ large NOTIFY fontsChanged FINAL) + Q_PROPERTY(QFont medium READ medium NOTIFY fontsChanged FINAL) + Q_PROPERTY(QFont small READ small NOTIFY fontsChanged FINAL) + +public: + explicit FontStyleBase(QObject* parent = nullptr) + : QObject(parent) {} + + void bind(FontStyleConfig* cfg); + + [[nodiscard]] QFont large() const; + [[nodiscard]] QFont medium() const; + [[nodiscard]] QFont small() const; + +signals: + void fontsChanged(); + +protected: + virtual void rebuild(); + + static QFont buildFont(const FontConfig* cfg); + + FontStyleConfig* m_cfg = nullptr; + QFont m_large; + QFont m_medium; + QFont m_small; +}; + +class FontStyle : public FontStyleBase { + Q_OBJECT + QML_ANONYMOUS + + Q_PROPERTY(caelestia::config::FontBuilders* builders READ builders CONSTANT FINAL) + +public: + explicit FontStyle(QObject* parent = nullptr); + + [[nodiscard]] FontBuilders* builders() const; + +private: + FontBuilders* m_builders; +}; + +class IconFontStyle : public FontStyleBase { + Q_OBJECT + QML_ANONYMOUS + + Q_PROPERTY(QFont extraLarge READ extraLarge NOTIFY fontsChanged FINAL) + Q_PROPERTY(caelestia::config::FontBuilder builder READ builder NOTIFY fontsChanged FINAL) + +public: + explicit IconFontStyle(QObject* parent = nullptr) + : FontStyleBase(parent) {} + + void bind(IconFontStyleConfig* cfg); + + [[nodiscard]] QFont extraLarge() const; + [[nodiscard]] FontBuilder builder() const; + +protected: + void rebuild() override; + +private: + QFont m_extraLarge; +}; + +class FontTokens : public QObject { + Q_OBJECT + QML_ANONYMOUS + + Q_PROPERTY(caelestia::config::FontStyle* headline READ headline CONSTANT FINAL) + Q_PROPERTY(caelestia::config::FontStyle* title READ title CONSTANT FINAL) + Q_PROPERTY(caelestia::config::FontStyle* body READ body CONSTANT FINAL) + Q_PROPERTY(caelestia::config::FontStyle* label READ label CONSTANT FINAL) + Q_PROPERTY(caelestia::config::FontStyle* mono READ mono CONSTANT FINAL) + Q_PROPERTY(caelestia::config::IconFontStyle* icon READ icon CONSTANT FINAL) + Q_PROPERTY(caelestia::config::FontBuilder clock READ clock NOTIFY clockChanged FINAL) + +public: + explicit FontTokens(QObject* parent = nullptr); + + void bindFont(AppearanceFont* font); + + [[nodiscard]] FontStyle* headline() const; + [[nodiscard]] FontStyle* title() const; + [[nodiscard]] FontStyle* body() const; + [[nodiscard]] FontStyle* label() const; + [[nodiscard]] FontStyle* mono() const; + [[nodiscard]] IconFontStyle* icon() const; + [[nodiscard]] FontBuilder clock() const; + +signals: + void clockChanged(); + +private: + void rebuildClock(); + + AppearanceFont* m_font = nullptr; + FontStyle* m_headline; + FontStyle* m_title; + FontStyle* m_body; + FontStyle* m_label; + FontStyle* m_mono; + IconFontStyle* m_icon; + QFont m_clock; +}; + +} // namespace caelestia::config diff --git a/plugin/src/Caelestia/Config/fontbuilder.cpp b/plugin/src/Caelestia/Config/fontbuilder.cpp index 7e1bbb0e..eb3a31d6 100644 --- a/plugin/src/Caelestia/Config/fontbuilder.cpp +++ b/plugin/src/Caelestia/Config/fontbuilder.cpp @@ -1,8 +1,5 @@ #include "fontbuilder.hpp" -#include -#include - namespace caelestia::config { FontBuilder::FontBuilder(QFont font) diff --git a/plugin/src/Caelestia/Config/tokensattached.cpp b/plugin/src/Caelestia/Config/tokensattached.cpp index 16f863b8..d7225981 100644 --- a/plugin/src/Caelestia/Config/tokensattached.cpp +++ b/plugin/src/Caelestia/Config/tokensattached.cpp @@ -2,6 +2,7 @@ #include "anim.hpp" #include "appearanceconfig.hpp" #include "config.hpp" +#include "font.hpp" #include "monitorconfigmanager.hpp" #include "tokens.hpp" @@ -23,8 +24,10 @@ const AppearanceConfig* resolveAppearance(GlobalConfig* config, bool complete, c Tokens::Tokens(QObject* parent) : QQuickAttachedPropertyPropagator(parent) + , m_font(new FontTokens(this)) , m_anim(new AnimTokens(this)) { bindAnim(); + bindFont(); initialize(); } @@ -52,6 +55,7 @@ void Tokens::inheritScreen(const QString& screen) { m_tokens = MonitorConfigManager::instance()->tokensForScreen(m_screen); } + bindFont(); propagateScreen(); emit sourceChanged(); } @@ -78,6 +82,11 @@ void Tokens::bindAnim() { m_anim->bindCurves(TokenConfig::instance()->appearance()->curves()); } +void Tokens::bindFont() { + auto* appearance = m_config ? m_config->appearance() : GlobalConfig::instance()->appearance(); + m_font->bindFont(appearance->font()); +} + #define TOKENS_ATTACHED_GETTER(Type, name) \ const Type* Tokens::name() const { \ auto* a = resolveAppearance(m_config, m_complete, #name, parent()); \ @@ -87,7 +96,6 @@ void Tokens::bindAnim() { TOKENS_ATTACHED_GETTER(AppearanceRounding, rounding) TOKENS_ATTACHED_GETTER(AppearanceSpacing, spacing) TOKENS_ATTACHED_GETTER(AppearancePadding, padding) -TOKENS_ATTACHED_GETTER(AppearanceFont, font) #undef TOKENS_ATTACHED_GETTER @@ -103,6 +111,10 @@ const SizeTokens* Tokens::sizes() const { return TokenConfig::instance()->sizes(); } +const FontTokens* Tokens::font() const { + return m_font; +} + const AnimTokens* Tokens::anim() const { return m_anim; } diff --git a/plugin/src/Caelestia/Config/tokensattached.hpp b/plugin/src/Caelestia/Config/tokensattached.hpp index 810b0c0d..623b2adc 100644 --- a/plugin/src/Caelestia/Config/tokensattached.hpp +++ b/plugin/src/Caelestia/Config/tokensattached.hpp @@ -7,11 +7,11 @@ namespace caelestia::config { class AnimTokens; -class AppearanceFont; class AppearancePadding; class AppearanceRounding; class AppearanceSpacing; class AppearanceTransparency; +class FontTokens; class GlobalConfig; class SizeTokens; class TokenConfig; @@ -24,15 +24,16 @@ class Tokens : public QQuickAttachedPropertyPropagator, public QQmlParserStatus QML_ATTACHED(Tokens) Q_MOC_INCLUDE("anim.hpp") Q_MOC_INCLUDE("appearanceconfig.hpp") + Q_MOC_INCLUDE("font.hpp") Q_MOC_INCLUDE("tokens.hpp") Q_PROPERTY(QString screen READ screen WRITE inheritScreen NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::AppearanceRounding* rounding READ rounding NOTIFY sourceChanged) 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::AppearanceTransparency* transparency READ transparency NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::SizeTokens* sizes READ sizes NOTIFY sourceChanged) + Q_PROPERTY(const caelestia::config::FontTokens* font READ font NOTIFY sourceChanged) Q_PROPERTY(const caelestia::config::AnimTokens* anim READ anim NOTIFY sourceChanged) public: @@ -44,10 +45,10 @@ public: [[nodiscard]] const AppearanceRounding* rounding() const; [[nodiscard]] const AppearanceSpacing* spacing() const; [[nodiscard]] const AppearancePadding* padding() const; - [[nodiscard]] const AppearanceFont* font() const; [[nodiscard]] const AppearanceTransparency* transparency() const; [[nodiscard]] const SizeTokens* sizes() const; + [[nodiscard]] const FontTokens* font() const; [[nodiscard]] const AnimTokens* anim() const; [[nodiscard]] Q_INVOKABLE static TokenConfig* forScreen(const QString& screen); @@ -67,11 +68,13 @@ private: void propagateScreen(); void bindAnim(); + void bindFont(); bool m_complete = false; QString m_screen; GlobalConfig* m_config = nullptr; TokenConfig* m_tokens = nullptr; + FontTokens* m_font = nullptr; AnimTokens* m_anim = nullptr; };