跳转至

ui/core/material/cfmaterial_motion.h

Material Design 3 Motion Scheme with EmbeddedTokenRegistry. More...

Namespaces

Name
cf
cf::ui
cf::ui::core
cf::ui::core::tokens

Classes

Name
struct cf::ui::core::MotionSpec
Motion specification structure.
struct cf::ui::core::MotionPresets
Static motion preset functions.
class cf::ui::core::MaterialMotionScheme
Material Design 3 Motion Scheme with EmbeddedTokenRegistry.
struct cf::ui::core::MaterialMotionScheme::MotionPresetsGroup
Motion presets group structure.

Detailed Description

Material Design 3 Motion Scheme with EmbeddedTokenRegistry.

Author: Charliechen114514 (chengh1922@mails.jlu.edu.cn)

Version: 0.1

Date: 2026-02-26

Copyright: Copyright © 2026

Implements the complete Material Design 3 motion system with duration, easing, and delay specifications. Motion specs are stored in an embedded registry for independent scheme instances.

Source code

#pragma once
#include <QEasingCurve>
#include <string>
#include <unordered_map>

#include "../motion_spec.h"
#include "../token.hpp"
#include "../token/motion/cfmaterial_motion_token_literals.h"
#include "base/easing.h"
#include "base/hash/constexpr_fnv1a.hpp"
#include "export.h"

namespace cf::ui::core {

// =============================================================================
// Motion Token Type Aliases - Material Motion System
// =============================================================================
namespace tokens {
using namespace cf::ui::core::token::literals;

// Duration tokens
using ShortEnterDurationToken = StaticToken<int, cf::hash::fnv1a64(MOTION_SHORT_ENTER_DURATION)>;
using ShortExitDurationToken = StaticToken<int, cf::hash::fnv1a64(MOTION_SHORT_EXIT_DURATION)>;
using MediumEnterDurationToken = StaticToken<int, cf::hash::fnv1a64(MOTION_MEDIUM_ENTER_DURATION)>;
using MediumExitDurationToken = StaticToken<int, cf::hash::fnv1a64(MOTION_MEDIUM_EXIT_DURATION)>;
using LongEnterDurationToken = StaticToken<int, cf::hash::fnv1a64(MOTION_LONG_ENTER_DURATION)>;
using LongExitDurationToken = StaticToken<int, cf::hash::fnv1a64(MOTION_LONG_EXIT_DURATION)>;
using StateChangeDurationToken = StaticToken<int, cf::hash::fnv1a64(MOTION_STATE_CHANGE_DURATION)>;
using RippleExpandDurationToken =
    StaticToken<int, cf::hash::fnv1a64(MOTION_RIPPLE_EXPAND_DURATION)>;
using RippleFadeDurationToken = StaticToken<int, cf::hash::fnv1a64(MOTION_RIPPLE_FADE_DURATION)>;

// Easing tokens (stored as int enum values)
using ShortEnterEasingToken = StaticToken<int, cf::hash::fnv1a64(MOTION_SHORT_ENTER_EASING)>;
using ShortExitEasingToken = StaticToken<int, cf::hash::fnv1a64(MOTION_SHORT_EXIT_EASING)>;
using MediumEnterEasingToken = StaticToken<int, cf::hash::fnv1a64(MOTION_MEDIUM_ENTER_EASING)>;
using MediumExitEasingToken = StaticToken<int, cf::hash::fnv1a64(MOTION_MEDIUM_EXIT_EASING)>;
using LongEnterEasingToken = StaticToken<int, cf::hash::fnv1a64(MOTION_LONG_ENTER_EASING)>;
using LongExitEasingToken = StaticToken<int, cf::hash::fnv1a64(MOTION_LONG_EXIT_EASING)>;
using StateChangeEasingToken = StaticToken<int, cf::hash::fnv1a64(MOTION_STATE_CHANGE_EASING)>;
using RippleExpandEasingToken = StaticToken<int, cf::hash::fnv1a64(MOTION_RIPPLE_EXPAND_EASING)>;
using RippleFadeEasingToken = StaticToken<int, cf::hash::fnv1a64(MOTION_RIPPLE_FADE_EASING)>;

} // namespace tokens

// =============================================================================
// Motion Data Structure
// =============================================================================

struct MotionSpec {
    int durationMs;                    
    cf::ui::base::Easing::Type easing; 
    int delayMs = 0;                   

    QEasingCurve toEasingCurve() const { return cf::ui::base::Easing::fromEasingType(easing); }

    bool operator==(const MotionSpec& other) const {
        return durationMs == other.durationMs && easing == other.easing && delayMs == other.delayMs;
    }

    bool operator!=(const MotionSpec& other) const { return !(*this == other); }
};

// =============================================================================
// Motion Presets
// =============================================================================

struct MotionPresets {
    static MotionSpec shortEnter() {
        return {200, cf::ui::base::Easing::Type::EmphasizedDecelerate, 0};
    }

    static MotionSpec shortExit() {
        return {150, cf::ui::base::Easing::Type::EmphasizedAccelerate, 0};
    }

    static MotionSpec mediumEnter() {
        return {300, cf::ui::base::Easing::Type::EmphasizedDecelerate, 0};
    }

    static MotionSpec mediumExit() {
        return {250, cf::ui::base::Easing::Type::EmphasizedAccelerate, 0};
    }

    static MotionSpec longEnter() { return {450, cf::ui::base::Easing::Type::Emphasized, 0}; }

    static MotionSpec longExit() { return {400, cf::ui::base::Easing::Type::Emphasized, 0}; }

    static MotionSpec stateChange() { return {200, cf::ui::base::Easing::Type::Standard, 0}; }

    static MotionSpec rippleExpand() { return {400, cf::ui::base::Easing::Type::Standard, 0}; }

    static MotionSpec rippleFade() { return {150, cf::ui::base::Easing::Type::Linear, 0}; }
};

// =============================================================================
// Material Motion Scheme
// =============================================================================

class CF_UI_EXPORT MaterialMotionScheme : public IMotionSpec {
  public:
    MaterialMotionScheme();
    ~MaterialMotionScheme() override = default;

    // Non-copyable, movable
    MaterialMotionScheme(const MaterialMotionScheme&) = delete;
    MaterialMotionScheme& operator=(const MaterialMotionScheme&) = delete;
    MaterialMotionScheme(MaterialMotionScheme&&) noexcept = default;
    MaterialMotionScheme& operator=(MaterialMotionScheme&&) noexcept = default;

    int queryDuration(const char* name) override;

    int queryEasing(const char* name) override;

    int queryDelay(const char* name) override;

    MotionSpec getMotionSpec(const char* name);

    EmbeddedTokenRegistry& registry() { return registry_; }

    const EmbeddedTokenRegistry& registry() const { return registry_; }

    struct MotionPresetsGroup {
        MotionSpec shortEnter;   
        MotionSpec shortExit;    
        MotionSpec mediumEnter;  
        MotionSpec mediumExit;   
        MotionSpec longEnter;    
        MotionSpec longExit;     
        MotionSpec stateChange;  
        MotionSpec rippleExpand; 
        MotionSpec rippleFade;   
    };

    [[nodiscard]] MotionPresetsGroup presets() const {
        return MotionPresetsGroup{MotionPresets::shortEnter(),  MotionPresets::shortExit(),
                                  MotionPresets::mediumEnter(), MotionPresets::mediumExit(),
                                  MotionPresets::longEnter(),   MotionPresets::longExit(),
                                  MotionPresets::stateChange(), MotionPresets::rippleExpand(),
                                  MotionPresets::rippleFade()};
    }

  private:
    EmbeddedTokenRegistry registry_;
    mutable std::unordered_map<std::string, MotionSpec> spec_cache_;
};

} // namespace cf::ui::core

Updated on 2026-03-09 at 10:14:01 +0000