跳转至

ui/widget/application_support/application.cpp

CF Desktop Application class implementation. More...

Namespaces

Name
cf
cf::ui
cf::ui::widget
cf::ui::widget::application_support

Detailed Description

CF Desktop Application class implementation.

Author: N/A

Version: 0.1

Date: 2026-02-28

Copyright: Copyright © 2026

Source code

#include "application.h"
#include <stdexcept>

namespace cf::ui::widget::application_support {

Application::Application(int& argc, char** argv) : QApplication(argc, argv), initialized_(false) {
    // Connect to ThemeManager's themeChanged signal
    auto& tm = core::ThemeManager::instance();
    connect(&tm, &core::ThemeManager::themeChanged, this, &Application::onThemeManagerChanged);
}

Application::~Application() {
    // Cleanup: animationFactory_ must be destroyed before theme cleanup
    // (since it holds references to the theme)
    animationFactory_.reset();
}

void Application::init() {
    if (initialized_) {
        throw std::runtime_error(
            "Application::init() already called. Initialization can only be done once.");
    }

    // Initialize animation factory with current theme
    initializeAnimationFactory();
    initialized_ = true;
}

Application* Application::instance() {
    return qobject_cast<Application*>(QApplication::instance());
}

core::ThemeManager* Application::themeManager() {
    return &core::ThemeManager::instance();
}

cf::WeakPtr<components::ICFAnimationManagerFactory> Application::animationFactory() {
    if (auto* app = instance()) {
        return app->animationFactory_->GetWeakPtr();
    }
    return {};
}

const core::ICFTheme& Application::theme(const std::string& themeToken) const {
    return core::ThemeManager::instance().theme(themeToken);
}

void Application::setTheme(const std::string& themeToken) {
    core::ThemeManager::instance().setThemeTo(themeToken);
}

const core::ICFTheme& Application::currentTheme() const {
    auto& tm = core::ThemeManager::instance();
    const auto& name = tm.currentThemeName();

    if (name.empty()) {
        // If no theme has been set, throw exception
        // TODO: Support dynamic loading of default themes
        throw std::runtime_error("No theme set. Use MaterialApplication for automatic theme "
                                 "registration, or call setTheme() before accessing themes.");
    }

    return tm.theme(name);
}

cf::WeakPtr<components::ICFAbstractAnimation>
Application::animation(const std::string& animationToken) {
    if (animationFactory_) {
        return animationFactory_->getAnimation(animationToken.c_str());
    }
    return {};
}

void Application::setAnimationsEnabled(bool enabled) {
    if (animationFactory_) {
        animationFactory_->setEnabledAll(enabled);
        emit animationsEnabledChanged(enabled);
    }
}

bool Application::animationsEnabled() const {
    return animationFactory_ ? animationFactory_->isAllEnabled() : false;
}

void Application::initializeAnimationFactory() {
    auto& tm = core::ThemeManager::instance();
    const auto& themeName = tm.currentThemeName();
    const auto& currentTheme = this->currentTheme();

    animationFactory_ = createAnimationFactory(themeName, currentTheme, this);
}

void Application::onThemeManagerChanged(const core::ICFTheme& newTheme) {
    // Recreate animation factory with new theme
    // This is necessary because factories store a reference to theme
    auto oldEnabled = animationsEnabled();
    auto& tm = core::ThemeManager::instance();
    const auto& themeName = tm.currentThemeName();

    animationFactory_ = createAnimationFactory(themeName, newTheme, this);

    animationFactory_->setEnabledAll(oldEnabled);

    // Forward the signal
    emit themeChanged(newTheme);
}

// =============================================================================
// Animation Factory Registry
// =============================================================================

std::unordered_map<std::string, Application::AnimationFactoryMaker>&
Application::animationFactoryRegistry() {
    static std::unordered_map<std::string, AnimationFactoryMaker> registry;
    return registry;
}

bool Application::registerAnimationFactoryType(const std::string& themePrefix,
                                               AnimationFactoryMaker maker) {
    auto& registry = animationFactoryRegistry();

    if (registry.find(themePrefix) != registry.end()) {
        return false; // Already registered
    }

    registry[themePrefix] = std::move(maker);
    return true;
}

void Application::unregisterAnimationFactoryType(const std::string& themePrefix) {
    auto& registry = animationFactoryRegistry();
    registry.erase(themePrefix);
}

bool Application::registerAnimationFactory(const std::string& themePrefix,
                                           AnimationFactoryMaker maker) {
    return registerAnimationFactoryType(themePrefix, std::move(maker));
}

void Application::unregisterAnimationFactory(const std::string& themePrefix) {
    unregisterAnimationFactoryType(themePrefix);
}

std::unique_ptr<components::ICFAnimationManagerFactory>
Application::createAnimationFactory(const std::string& themeName, const core::ICFTheme& theme,
                                    QObject* parent) {
    auto& registry = animationFactoryRegistry();

    // Find the best matching prefix
    // themeName format: "theme.material.light", "theme.fluent.dark"
    // We try to match "theme.material", "theme.fluent", etc.

    // Split by '.' and check for prefixes
    // Try longest prefixes first (most specific)
    std::string bestMatch;
    for (const auto& [prefix, maker] : registry) {
        if (themeName.size() >= prefix.size() && themeName.compare(0, prefix.size(), prefix) == 0) {
            // Check if this is a better (longer) match
            if (prefix.size() > bestMatch.size()) {
                bestMatch = prefix;
            }
        }
    }

    if (!bestMatch.empty()) {
        // Found a matching factory
        return registry[bestMatch](theme, parent);
    }

    // No matching factory found - throw exception
    // TODO: Support dynamic loading of animation factories
    throw std::runtime_error(
        "No animation factory registered for theme '" + themeName +
        "'. Register an animation factory for this theme prefix, "
        "or use MaterialApplication which includes default Material factory support.");
}

} // namespace cf::ui::widget::application_support

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