Added demos.
This commit is contained in:
466
doc/claude_fluent_light/fluentwidget.h
Normal file
466
doc/claude_fluent_light/fluentwidget.h
Normal file
@@ -0,0 +1,466 @@
|
||||
// FluentWidget.h
|
||||
#ifndef FLUENTWIDGET_H
|
||||
#define FLUENTWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QGraphicsDropShadowEffect>
|
||||
#include <QLineEdit>
|
||||
#include <QSlider>
|
||||
#include <QCheckBox>
|
||||
#include <QDebug>
|
||||
|
||||
// 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
|
||||
Reference in New Issue
Block a user