跳转至

example/gui/theme/MaterialGalleryMainWindow.cpp

Main window for the Material Design 3 Gallery - Implementation. More...

Namespaces

Name
cf
cf::ui
cf::ui::gallery

Detailed Description

Main window for the Material Design 3 Gallery - Implementation.

Author: CFDesktop Team

Version: 0.1

Date: 2026-02-28

Source code

#include "MaterialGalleryMainWindow.h"
#include "ColorSchemePage.h"
#include "MotionSpecPage.h"
#include "RadiusScalePage.h"
#include "ThemePageWidget.h"
#include "ThemeSidebar.h"
#include "TypographyPage.h"

#include "ui/core/material/cfmaterial_scheme.h"
#include "ui/core/material/material_factory.hpp"
#include "ui/core/material/material_factory_class.h"
#include "ui/core/theme_factory.h"
#include "ui/core/theme_manager.h"
#include "ui/core/token/theme_name/material_theme_name.h"

#include <QMouseEvent>
#include <QPainter>

// Global namespace alias for token literals
namespace token_literals = ::cf::ui::core::token::literals;

namespace cf::ui::gallery {

// =============================================================================
// ThemeSwitch Implementation
// =============================================================================

ThemeSwitch::ThemeSwitch(QWidget* parent) : QWidget(parent) {
    setFixedSize(56, 28);

    animation_ = new QPropertyAnimation(this, "knobPosition", this);
    animation_->setDuration(200);
    animation_->setEasingCurve(QEasingCurve::OutCubic);
}

void ThemeSwitch::setDark(bool dark) {
    if (isDark_ != dark) {
        isDark_ = dark;
        float targetPos = dark ? 1.0f : 0.0f;
        animation_->stop();
        animation_->setStartValue(knobPosition_);
        animation_->setEndValue(targetPos);
        animation_->start();
        emit themeChanged(isDark_);
    }
}

void ThemeSwitch::setKnobPosition(float pos) {
    knobPosition_ = qBound(0.0f, pos, 1.0f);
    update();
    emit knobPositionChanged();
}

void ThemeSwitch::paintEvent(QPaintEvent*) {
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    QRectF r = rect();
    qreal radius = r.height() / 2;

    // Track background
    QColor trackColor = isDark_ ? QColor(103, 80, 164) : QColor(180, 180, 180);
    QPainterPath trackPath;
    trackPath.addRoundedRect(r, radius, radius);
    painter.setPen(Qt::NoPen);
    painter.setBrush(trackColor);
    painter.drawPath(trackPath);

    // Knob
    qreal knobDiameter = r.height() - 6;
    qreal knobX = 3 + knobPosition_ * (r.width() - knobDiameter - 6);
    QRectF knobRect(knobX, 3, knobDiameter, knobDiameter);

    QPainterPath knobPath;
    knobPath.addEllipse(knobRect);

    // Knob shadow
    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(0, 0, 0, 30));
    painter.drawEllipse(knobRect.translated(0, 1));

    // Knob
    painter.setBrush(Qt::white);
    painter.drawPath(knobPath);

    // Icon on knob (sun or moon)
    painter.setPen(isDark_ ? QColor(60, 60, 60) : QColor(255, 200, 50));
    QFont iconFont("Segoe UI Emoji", 11);
    painter.setFont(iconFont);
    QString icon = isDark_ ? "🌙" : "☀️";
    painter.drawText(knobRect, Qt::AlignCenter, icon);
}

void ThemeSwitch::mousePressEvent(QMouseEvent*) {
    setDark(!isDark_);
}

// =============================================================================
// MaterialGalleryMainWindow Implementation
// =============================================================================

MaterialGalleryMainWindow::MaterialGalleryMainWindow(QWidget* parent) : QMainWindow(parent) {

    setupThemeManager();
    setupUI();
    createHeader();

    // Create pages and add to stacked widget
    colorSchemePage_ = new ColorSchemePage(this);
    motionSpecPage_ = new MotionSpecPage(this);
    radiusScalePage_ = new RadiusScalePage(this);
    typographyPage_ = new TypographyPage(this);

    contentStack_->addWidget(colorSchemePage_);
    contentStack_->addWidget(motionSpecPage_);
    contentStack_->addWidget(radiusScalePage_);
    contentStack_->addWidget(typographyPage_);

    // Set initial page
    contentStack_->setCurrentIndex(0);

    themeManager_->setThemeTo(token_literals::MATERIAL_THEME_LIGHT);

    // Apply initial theme
    if (themeManager_) {
        auto& theme = themeManager_->theme(token_literals::MATERIAL_THEME_LIGHT);
        applyThemeToAllPages(theme);
        sidebar_->applyTheme(theme);
    }
}

MaterialGalleryMainWindow::~MaterialGalleryMainWindow() {
    // Cleanup is handled by Qt's parent-child system
}

void MaterialGalleryMainWindow::setupThemeManager() {
    themeManager_ = &core::ThemeManager::instance();

    // Register MaterialFactory for both light and dark themes
    auto installMaterialTheme = []() { return std::make_unique<core::MaterialFactory>(); };
    // Register both theme names - the same factory handles both
    themeManager_->insert_one(token_literals::MATERIAL_THEME_LIGHT, installMaterialTheme);
    themeManager_->insert_one(token_literals::MATERIAL_THEME_DARK, installMaterialTheme);

    // Install main window for theme updates
    themeManager_->install_widget(this);

    // Connect themeChanged signal
    connect(themeManager_, &core::ThemeManager::themeChanged, this,
            &MaterialGalleryMainWindow::onThemeManagerChanged);
}

void MaterialGalleryMainWindow::setupUI() {
    // Window setup
    setWindowTitle("Material Design 3 Gallery");
    resize(1200, 800);
    setMinimumSize(900, 600);

    // Central widget
    centralWidget_ = new QWidget(this);
    setCentralWidget(centralWidget_);

    // Main horizontal layout (sidebar + content)
    mainLayout_ = new QHBoxLayout(centralWidget_);
    mainLayout_->setContentsMargins(0, 0, 0, 0);
    mainLayout_->setSpacing(0);

    // Sidebar
    sidebar_ = new ThemeSidebar(this);
    mainLayout_->addWidget(sidebar_);

    // Connect sidebar signal
    connect(sidebar_, &ThemeSidebar::tabClicked, this, &MaterialGalleryMainWindow::onTabClicked);

    // Content area (vertical layout with header + stacked widget)
    QWidget* contentWidget = new QWidget(this);
    contentLayout_ = new QVBoxLayout(contentWidget);
    contentLayout_->setContentsMargins(0, 0, 0, 0);
    contentLayout_->setSpacing(0);

    mainLayout_->addWidget(contentWidget, 1); // Stretch factor 1

    // Content stack for page switching
    contentStack_ = new QStackedWidget(this);
    contentLayout_->addWidget(contentStack_, 1); // Stretch factor 1
}

void MaterialGalleryMainWindow::createHeader() {
    // Header layout
    headerLayout_ = new QHBoxLayout();
    headerLayout_->setContentsMargins(20, 16, 20, 16);
    headerLayout_->setSpacing(16);
    contentLayout_->addLayout(headerLayout_);

    // Title
    titleLabel_ = new QLabel("Material Design 3 Gallery", this);
    QFont titleFont("Segoe UI", 16, QFont::Bold);
    titleLabel_->setFont(titleFont);
    titleLabel_->setStyleSheet("QLabel { color: #1C1B1F; }");
    headerLayout_->addWidget(titleLabel_);
    headerLayout_->addStretch();

    // Theme label
    themeLabel_ = new QLabel("☀️ Light", this);
    QFont themeFont("Segoe UI", 10);
    themeLabel_->setFont(themeFont);
    themeLabel_->setStyleSheet("QLabel { color: #49454F; }");
    headerLayout_->addWidget(themeLabel_);

    // Theme switch
    themeSwitch_ = new ThemeSwitch(this);
    headerLayout_->addWidget(themeSwitch_);

    connect(themeSwitch_, &ThemeSwitch::themeChanged, this,
            &MaterialGalleryMainWindow::onThemeChanged);
}

void MaterialGalleryMainWindow::applyThemeToAllPages(const core::ICFTheme& theme) {
    colorSchemePage_->applyTheme(theme);
    motionSpecPage_->applyTheme(theme);
    radiusScalePage_->applyTheme(theme);
    typographyPage_->applyTheme(theme);
    sidebar_->applyTheme(theme);

    // Update header colors
    auto& colorScheme = static_cast<const core::MaterialColorScheme&>(theme.color_scheme());
    QColor onSurface = colorScheme.queryColor("md.onSurface");
    QColor bg = colorScheme.queryColor("md.background");

    titleLabel_->setStyleSheet(QString("QLabel { color: %1; }").arg(onSurface.name()));
    themeLabel_->setStyleSheet(QString("QLabel { color: %1; }").arg(onSurface.name()));

    // Update central widget background
    centralWidget_->setAutoFillBackground(true);
    QPalette pal = centralWidget_->palette();
    pal.setColor(QPalette::Window, bg);
    centralWidget_->setPalette(pal);
}

void MaterialGalleryMainWindow::onTabClicked(int index) {
    contentStack_->setCurrentIndex(index);
}

void MaterialGalleryMainWindow::onThemeChanged(bool isDark) {
    isDarkTheme_ = isDark;

    // Update theme label
    themeLabel_->setText(isDark ? "🌙 Dark" : "☀️ Light");

    // Switch theme via ThemeManager
    const char* themeName =
        isDark ? token_literals::MATERIAL_THEME_DARK : token_literals::MATERIAL_THEME_LIGHT;
    themeManager_->setThemeTo(themeName);
}

void MaterialGalleryMainWindow::onThemeManagerChanged(const core::ICFTheme& theme) {
    applyThemeToAllPages(theme);
}

void MaterialGalleryMainWindow::resizeEvent(QResizeEvent* event) {
    QMainWindow::resizeEvent(event);
    // Handle responsive layout if needed
}

} // namespace cf::ui::gallery

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