// FluentWidget.h #ifndef FLUENTWIDGET_H #define FLUENTWIDGET_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Fluent Card Widget mit Hover-Effekt class FluentCard : public QWidget { Q_OBJECT Q_PROPERTY(qreal elevationFactor READ elevationFactor WRITE setElevationFactor) public: FluentCard(const QString& title, const QString& subtitle, const QColor& accentColor, QWidget* parent = nullptr) : QWidget(parent), m_title(title), m_subtitle(subtitle), m_accentColor(accentColor), m_elevationFactor(0.0) { setFixedSize(280, 140); setMouseTracking(true); } qreal elevationFactor() const { return m_elevationFactor; } void setElevationFactor(qreal factor) { qDebug() << " --- set: " << factor; m_elevationFactor = factor; update(); } protected: void paintEvent(QPaintEvent*) override { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // Draw shadow manually (multiple layers for smooth shadow) int shadowSize = 2 + m_elevationFactor * 10; int shadowOffset = m_elevationFactor * 4; for (int i = shadowSize; i > 0; i--) { int alpha = 15 * (1.0 - i / (qreal)shadowSize); QColor shadowColor(0, 0, 0, alpha); painter.setPen(Qt::NoPen); painter.setBrush(shadowColor); painter.drawRoundedRect( rect().adjusted(-i, -i + shadowOffset, i, i + shadowOffset), 8 + i, 8 + i ); } // Card background painter.setBrush(QColor(43, 43, 43)); // Dark surface painter.setPen(QPen(QColor(63, 63, 63), 1)); // Border painter.drawRoundedRect(rect().adjusted(1, 1, -1, -1), 8, 8); // Accent icon circle QColor iconBg = m_accentColor; iconBg.setAlpha(40); painter.setBrush(iconBg); painter.setPen(Qt::NoPen); painter.drawEllipse(20, 20, 48, 48); // Icon (simplified) painter.setPen(m_accentColor); QFont iconFont = font(); iconFont.setPointSize(24); iconFont.setBold(true); painter.setFont(iconFont); painter.drawText(QRect(20, 20, 48, 48), Qt::AlignCenter, "📄"); // Title painter.setPen(Qt::white); QFont titleFont = font(); titleFont.setPointSize(12); titleFont.setWeight(QFont::DemiBold); painter.setFont(titleFont); painter.drawText(QRect(20, 80, width() - 40, 25), Qt::AlignLeft | Qt::AlignVCenter, m_title); // Subtitle painter.setPen(QColor(176, 176, 176)); QFont subFont = font(); subFont.setPointSize(9); painter.setFont(subFont); painter.drawText(QRect(20, 105, width() - 40, 20), Qt::AlignLeft | Qt::AlignVCenter, m_subtitle); } void enterEvent(QEnterEvent*) override { QPropertyAnimation* anim = new QPropertyAnimation(this, "elevationFactor"); anim->setDuration(200); anim->setEasingCurve(QEasingCurve::OutCubic); anim->setStartValue(m_elevationFactor); anim->setEndValue(1.0); anim->start(QAbstractAnimation::DeleteWhenStopped); } void leaveEvent(QEvent*) override { QPropertyAnimation* anim = new QPropertyAnimation(this, "elevationFactor"); anim->setDuration(200); anim->setEasingCurve(QEasingCurve::OutCubic); anim->setStartValue(m_elevationFactor); anim->setEndValue(0.0); anim->start(QAbstractAnimation::DeleteWhenStopped); } private: QString m_title; QString m_subtitle; QColor m_accentColor; qreal m_elevationFactor; }; // Fluent Button mit Acrylic-Style class FluentButton : public QPushButton { Q_OBJECT public: enum ButtonStyle { Primary, Secondary, Accent }; FluentButton(const QString& text, ButtonStyle style = Secondary, QWidget* parent = nullptr) : QPushButton(text, parent), m_style(style), m_pressed(false) { setMinimumHeight(32); setCursor(Qt::PointingHandCursor); updateStyle(); } protected: void enterEvent(QEnterEvent*) override { updateStyle(true); } void leaveEvent(QEvent*) override { updateStyle(false); } void mousePressEvent(QMouseEvent* e) override { m_pressed = true; QPushButton::mousePressEvent(e); } void mouseReleaseEvent(QMouseEvent* e) override { m_pressed = false; QPushButton::mouseReleaseEvent(e); } private: void updateStyle(bool hovered = false) { QString style; switch (m_style) { case Primary: if (hovered) { style = "QPushButton { background-color: #106EBE; color: white; " "border: none; border-radius: 4px; font-size: 9pt; " "padding: 6px 16px; font-weight: 500; }"; } else { style = "QPushButton { background-color: #0078D4; color: white; " "border: none; border-radius: 4px; font-size: 9pt; " "padding: 6px 16px; font-weight: 500; }"; } break; case Secondary: if (hovered) { style = "QPushButton { background-color: #3A3A3A; color: white; " "border: 1px solid #5A5A5A; border-radius: 4px; " "font-size: 9pt; padding: 6px 16px; font-weight: 500; }"; } else { style = "QPushButton { background-color: #2B2B2B; color: white; " "border: 1px solid #3F3F3F; border-radius: 4px; " "font-size: 9pt; padding: 6px 16px; font-weight: 500; }"; } break; case Accent: if (hovered) { style = "QPushButton { background-color: rgba(0, 120, 212, 0.15); " "color: #60CDFF; border: none; border-radius: 4px; " "font-size: 9pt; padding: 6px 16px; font-weight: 500; }"; } else { style = "QPushButton { background-color: transparent; " "color: #60CDFF; border: none; border-radius: 4px; " "font-size: 9pt; padding: 6px 16px; font-weight: 500; }"; } break; } setStyleSheet(style); } ButtonStyle m_style; bool m_pressed; }; // Fluent Toggle Switch class FluentToggle : public QWidget { Q_OBJECT Q_PROPERTY(int position READ position WRITE setPosition) public: FluentToggle(QWidget* parent = nullptr) : QWidget(parent), m_checked(false), m_position(2) { setFixedSize(44, 20); setCursor(Qt::PointingHandCursor); } bool isChecked() const { return m_checked; } void setChecked(bool checked) { if (m_checked != checked) { m_checked = checked; animateToggle(); emit toggled(m_checked); } } int position() const { return m_position; } void setPosition(int pos) { m_position = pos; update(); } signals: void toggled(bool checked); protected: void paintEvent(QPaintEvent*) override { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // Track QColor trackColor = m_checked ? QColor("#0078D4") : QColor("#3F3F3F"); painter.setPen(Qt::NoPen); painter.setBrush(trackColor); painter.drawRoundedRect(rect(), 10, 10); // Thumb painter.setBrush(Qt::white); painter.drawEllipse(m_position, 2, 16, 16); } void mousePressEvent(QMouseEvent*) override { setChecked(!m_checked); } private: void animateToggle() { QPropertyAnimation* anim = new QPropertyAnimation(this, "position"); anim->setDuration(150); anim->setEasingCurve(QEasingCurve::InOutQuad); anim->setEndValue(m_checked ? 26 : 2); anim->start(QAbstractAnimation::DeleteWhenStopped); } bool m_checked; int m_position; }; // Main Fluent Widget class FluentWidget : public QWidget { Q_OBJECT public: FluentWidget(QWidget* parent = nullptr) : QWidget(parent) { setupUI(); applyFluentDarkTheme(); } private: void setupUI() { QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); // Title Bar QWidget* titleBar = createTitleBar(); mainLayout->addWidget(titleBar); // Content Area QWidget* content = new QWidget(); content->setStyleSheet("background-color: #202020;"); QVBoxLayout* contentLayout = new QVBoxLayout(content); contentLayout->setContentsMargins(40, 40, 40, 40); contentLayout->setSpacing(32); // Header QLabel* titleLabel = new QLabel("Fluent Design System"); titleLabel->setStyleSheet("color: white; font-size: 28pt; font-weight: 600;"); contentLayout->addWidget(titleLabel); QLabel* subtitleLabel = new QLabel("Modern Windows 11 inspired UI components"); subtitleLabel->setStyleSheet("color: #B0B0B0; font-size: 11pt;"); contentLayout->addWidget(subtitleLabel); contentLayout->addSpacing(20); // Cards Grid QWidget* cardsWidget = new QWidget(); QGridLayout* cardsLayout = new QGridLayout(cardsWidget); cardsLayout->setSpacing(20); cardsLayout->addWidget(new FluentCard("Documents", "128 files", QColor("#0078D4")), 0, 0); cardsLayout->addWidget(new FluentCard("Photos", "1,234 items", QColor("#8764B8")), 0, 1); cardsLayout->addWidget(new FluentCard("Music", "89 songs", QColor("#00B7C3")), 0, 2); cardsLayout->addWidget(new FluentCard("Videos", "24 clips", QColor("#E3008C")), 1, 0); cardsLayout->addWidget(new FluentCard("Downloads", "45 items", QColor("#00CC6A")), 1, 1); cardsLayout->addWidget(new FluentCard("Projects", "12 folders", QColor("#FF8C00")), 1, 2); contentLayout->addWidget(cardsWidget); contentLayout->addSpacing(20); // Controls Section QWidget* controlsPanel = createControlsPanel(); contentLayout->addWidget(controlsPanel); contentLayout->addStretch(); mainLayout->addWidget(content); } QWidget* createTitleBar() { QWidget* titleBar = new QWidget(); titleBar->setFixedHeight(48); titleBar->setStyleSheet("background-color: #2B2B2B; border-bottom: 1px solid #3F3F3F;"); QHBoxLayout* layout = new QHBoxLayout(titleBar); layout->setContentsMargins(16, 0, 16, 0); // App Icon QLabel* iconLabel = new QLabel("💎"); iconLabel->setStyleSheet("font-size: 20pt;"); layout->addWidget(iconLabel); QLabel* appName = new QLabel("Fluent Demo"); appName->setStyleSheet("color: white; font-size: 10pt; font-weight: 600;"); layout->addWidget(appName); layout->addStretch(); // Action Buttons QPushButton* searchBtn = new QPushButton("🔍"); searchBtn->setFixedSize(32, 32); searchBtn->setStyleSheet("QPushButton { background: transparent; color: white; " "border: none; border-radius: 4px; font-size: 14pt; }" "QPushButton:hover { background: #3A3A3A; }"); layout->addWidget(searchBtn); QPushButton* notifBtn = new QPushButton("🔔"); notifBtn->setFixedSize(32, 32); notifBtn->setStyleSheet("QPushButton { background: transparent; color: white; " "border: none; border-radius: 4px; font-size: 14pt; }" "QPushButton:hover { background: #3A3A3A; }"); layout->addWidget(notifBtn); QPushButton* userBtn = new QPushButton("👤"); userBtn->setFixedSize(32, 32); userBtn->setStyleSheet("QPushButton { background: transparent; color: white; " "border: none; border-radius: 4px; font-size: 14pt; }" "QPushButton:hover { background: #3A3A3A; }"); layout->addWidget(userBtn); return titleBar; } QWidget* createControlsPanel() { QWidget* panel = new QWidget(); panel->setStyleSheet("background-color: #2B2B2B; border: 1px solid #3F3F3F; " "border-radius: 8px;"); QVBoxLayout* layout = new QVBoxLayout(panel); layout->setContentsMargins(24, 24, 24, 24); layout->setSpacing(24); // Section Title QLabel* sectionTitle = new QLabel("Controls"); sectionTitle->setStyleSheet("color: white; font-size: 16pt; font-weight: 600;"); layout->addWidget(sectionTitle); // Buttons Demo QLabel* btnLabel = new QLabel("Buttons"); btnLabel->setStyleSheet("color: #B0B0B0; font-size: 10pt; font-weight: 600;"); layout->addWidget(btnLabel); QHBoxLayout* btnLayout = new QHBoxLayout(); btnLayout->setSpacing(12); btnLayout->addWidget(new FluentButton("Primary", FluentButton::Primary)); btnLayout->addWidget(new FluentButton("Secondary", FluentButton::Secondary)); btnLayout->addWidget(new FluentButton("Accent", FluentButton::Accent)); btnLayout->addStretch(); layout->addLayout(btnLayout); // Slider Demo QLabel* sliderLabel = new QLabel("Slider"); sliderLabel->setStyleSheet("color: #B0B0B0; font-size: 10pt; font-weight: 600;"); layout->addWidget(sliderLabel); QSlider* slider = new QSlider(Qt::Horizontal); slider->setRange(0, 100); slider->setValue(50); slider->setStyleSheet( "QSlider::groove:horizontal { background: #3F3F3F; height: 4px; " "border-radius: 2px; }" "QSlider::handle:horizontal { background: #0078D4; width: 16px; " "height: 16px; margin: -6px 0; border-radius: 8px; }" "QSlider::add-page:horizontal { background: #3F3F3F; }" "QSlider::sub-page:horizontal { background: #0078D4; }" ); layout->addWidget(slider); // Toggle Demo QLabel* toggleLabel = new QLabel("Toggles"); toggleLabel->setStyleSheet("color: #B0B0B0; font-size: 10pt; font-weight: 600;"); layout->addWidget(toggleLabel); QStringList toggleTexts = {"Enable notifications", "Dark mode", "Auto-save"}; for (const QString& text : toggleTexts) { QHBoxLayout* toggleRow = new QHBoxLayout(); QLabel* label = new QLabel(text); label->setStyleSheet("color: white; font-size: 10pt;"); toggleRow->addWidget(label); toggleRow->addStretch(); FluentToggle* toggle = new FluentToggle(); if (text == "Dark mode") { toggle->setChecked(true); } toggleRow->addWidget(toggle); layout->addLayout(toggleRow); } return panel; } void applyFluentDarkTheme() { setStyleSheet("QWidget { font-family: 'Segoe UI', 'Noto Sans', sans-serif; }"); } }; #endif // FLUENTWIDGET_H