#include "thememanager.h" #include #include #include #include #include static const QByteArray defaultConfig = R"Raw( { "color_mode": { "light": { }, "dark": { } }, "size_mode": { "small": { "font_base": "12px", "padding_base": "4px", "radius_base": "2px", "control_height": "20px" }, "medium": { "font_base": "14px", "padding_base": "8px", "radius_base": "4px", "control_height": "28px" }, "large": { "font_base": "16px", "padding_base": "12px", "radius_base": "6px", "control_height": "36px" } } } )Raw"; static const QByteArray defaultQss = R"Raw( /********************* 基础样式 *********************/ QWidget { font-family: "Segoe UI", "Microsoft YaHei"; font-size: @font_base; color: @colorText; background-color: @colorBgContainer; } * { font-family: "Segoe UI", "Microsoft YaHei"; font-size: @font_base; color: @colorText; background-color: @colorBgContainer; selection-background-color: @colorPrimaryBg; selection-color: @colorPrimaryText; } /********************* 窗口栏 *********************/ QWK--WindowBar[bar-active=true] { background-color: @colorPrimary; } QWK--WindowBar[bar-active=false] { background-color: @colorPrimaryBgHover; } QWK--WindowBar>QLabel#win-title-label { padding: 0; border: none; color: @colorText; background-color: transparent; min-height: 28px; } /* 系统按钮 */ QWK--WindowBar>QAbstractButton[system-button=true] { qproperty-iconSize: 12px 12px; min-width: 40px; border: none; padding: 0; background-color: transparent; } QWK--WindowBar>QAbstractButton#settings-button { qproperty-iconNormal: url(":/window-bar/more-line.svg"); qproperty-iconChecked: url(":/window-bar/more-line.svg"); qproperty-iconSize: 15px 15px; } QWK--WindowBar>QAbstractButton#settings-button:hover, QWK--WindowBar>QAbstractButton#settings-button:pressed { background-color: @colorPrimaryBgHover; } QWK--WindowBar>QAbstractButton#pin-button { qproperty-iconNormal: url(":/window-bar/pin.svg"); qproperty-iconChecked: url(":/window-bar/pin-fill.svg"); qproperty-iconSize: 15px 15px; } QWK--WindowBar>QAbstractButton#pin-button:hover, QWK--WindowBar>QAbstractButton#pin-button:pressed { background-color: @colorPrimaryBgHover; } QWK--WindowBar>QAbstractButton#min-button { qproperty-iconNormal: url(":/window-bar/minimize.svg"); } QWK--WindowBar>QAbstractButton#min-button:hover, QWK--WindowBar>QAbstractButton#min-button:pressed { background-color: @colorPrimaryBgHover; } QWK--WindowBar>QAbstractButton#max-button { qproperty-iconNormal: url(":/window-bar/maximize.svg"); qproperty-iconChecked: url(":/window-bar/restore.svg"); } QWK--WindowBar>QAbstractButton#max-button:hover, QWK--WindowBar>QAbstractButton#max-button:pressed { background-color: @colorPrimaryBgHover; } QWK--WindowBar>QAbstractButton#close-button { qproperty-iconNormal: url(":/window-bar/close.svg"); } QWK--WindowBar>QAbstractButton#close-button:hover, QWK--WindowBar>QAbstractButton#close-button:pressed { background-color: @colorError; } QWK--WindowBar>QAbstractButton#icon-button { qproperty-iconNormal: url(":/app/example.png"); qproperty-iconSize: 18px 18px; min-width: 40px; border: none; padding: 0; background-color: transparent; } /********************* 通用控件 *********************/ /* 按钮 */ QPushButton { background-color: @colorPrimary; color: @colorWhite; border: 1px solid @colorPrimaryBorder; border-radius: @radius_base; padding: @padding_base; min-height: @control_height; min-width: 80px; } QPushButton:hover { background-color: @colorPrimaryHover; border-color: @colorPrimaryBorderHover; } QPushButton:pressed { background-color: @colorPrimaryActive; border-color: @colorPrimaryBorder; } QPushButton:disabled { background-color: @colorFillQuaternary; border-color: @colorBorderSecondary; color: @colorTextQuaternary; } /* 标签 */ QLabel { background-color: transparent; padding: @padding_base; color: @colorText; } /* 输入控件 */ QLineEdit, QTextEdit, QPlainTextEdit, QComboBox, QSpinBox, QDateTimeEdit { background-color: @colorBgContainer; border: 1px solid @colorBorder; border-radius: @radius_base; padding: @padding_base; min-height: @control_height; color: @colorText; } QLineEdit:hover, QTextEdit:hover, QPlainTextEdit:hover, QComboBox:hover, QSpinBox:hover, QDateTimeEdit:hover { border-color: @colorPrimaryHover; background-color: @colorBgContainer; } QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus, QComboBox:focus, QSpinBox:focus, QDateTimeEdit:focus { border-color: @colorPrimary; background-color: @colorBgContainer; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: center right; width: 20px; border-left: 1px solid @colorBorder; } QComboBox QAbstractItemView { background: @colorBgContainer; border: 1px solid @colorBorder; selection-background-color: @colorPrimaryBg; selection-color: @colorPrimaryText; } /* 选择控件 */ QCheckBox, QRadioButton { background-color: transparent; color: @colorText; } QCheckBox::indicator, QRadioButton::indicator { width: 16px; height: 16px; } QCheckBox::indicator { border: 1px solid @colorBorder; border-radius: @radius_small; background: @colorBgContainer; } QCheckBox::indicator:checked { background-color: @colorPrimary; border-color: @colorPrimary; image: url(":/theme/checked.svg"); } QCheckBox:disabled { color: @colorTextQuaternary; } QRadioButton::indicator { border: 1px solid @colorBorder; border-radius: 8px; background: @colorBgContainer; } QRadioButton::indicator:checked { background-color: @colorPrimary; border-color: @colorPrimary; image: url(":/theme/radio-checked.svg"); } QRadioButton:disabled { color: @colorTextQuaternary; } /********************* 复杂控件 *********************/ /* 选项卡 */ QTabWidget::pane { border: 1px solid @colorBorder; border-radius: @radius_base; } QTabBar::tab { background: @colorBgContainer; border: 1px solid transparent; padding: @padding_base; margin-right: 4px; color: @colorText; } QTabBar::tab:selected { background: @colorPrimaryBg; border-bottom: 2px solid @colorPrimary; color: @colorPrimaryText; } QTabBar::tab:hover { background: @colorPrimaryBgHover; } /* 滚动条 */ /* 滚动条整体 */ QScrollBar:vertical { background: @colorFillQuaternary; /* 滚动条轨道背景 */ width: 10px; /* 垂直滚动条宽度 */ margin: 10px 0 10px 0; /* 上下留白 */ } QScrollBar:horizontal { background: @colorFillQuaternary; /* 滚动条轨道背景 */ height: 10px; /* 水平滚动条高度 */ margin: 0 10px 0 10px; /* 左右留白 */ } /* 滑块(Handle) */ QScrollBar::handle:vertical { background: #eaeaea; /* 滑块颜色 */ min-height: 30px; /* 垂直滑块最小高度(重要!) */ border-radius: 5px; /* 可选圆角 */ } QScrollBar::handle:horizontal { background: #eaeaea; /* 滑块颜色 */ min-width: 30px; /* 水平滑块最小宽度(重要!) */ border-radius: 5px; /* 可选圆角 */ } /* 滑块悬停/按下效果 */ QScrollBar::handle:vertical:hover, QScrollBar::handle:horizontal:hover { background: #b0b0b0; /* 悬停颜色 */ } QScrollBar::handle:vertical:pressed, QScrollBar::handle:horizontal:pressed { background: #b0b0b0; /* 按下颜色 */ } /* 上下/左右箭头按钮(隐藏箭头) */ QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical, QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { background: none; /* 隐藏箭头按钮背景 */ border: none; /* 移除边框 */ height: 0; /* 垂直箭头高度设为0 */ width: 0; /* 水平箭头宽度设为0 */ } /* 滚动条边缘的空白区域(Add-Page/Sub-Page) */ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; /* 移除默认灰色背景 */ } /* 树形视图 */ QTreeView { show-decoration-selected: 1; alternate-background-color: @colorBgContainer; } QTreeView::item:hover { background: @colorPrimaryBgHover; } QTreeView::item:selected { background: @colorPrimaryBg; color: @colorPrimaryText; } /********************* 列表视图 *********************/ QListView { outline: none; background-color: @colorBgContainer; alternate-background-color: @colorFillQuaternary; border: 1px solid @colorBorder; border-radius: @radius_base; padding: 2px; } QListView::item { border-radius: @radius_small; padding: @padding_base; margin: 1px; } QListView::item:hover { background: @colorPrimaryBgHover; } QListView::item:selected { background: @colorPrimaryBg; color: @colorPrimaryText; } QListView::item:selected:active { background: @colorPrimaryBg; color: @colorPrimaryText; } QListView::item:selected:!active { background: @colorFillSecondary; color: @colorTextSecondary; } /********************* 分组框 *********************/ QGroupBox { border: 1px solid @colorBorder; border-radius: @radius_base; margin-top: 16px; padding-top: 20px; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding: 0 4px; color: @colorTextSecondary; left: 12px; } QGroupBox { background-color: transparent; border: 1px solid @colorBorder; border-radius: @radius_base; margin-top: 1ex; } /********************* 滑块控件 *********************/ QSlider::groove:horizontal { height: 4px; background: @colorFillQuaternary; border-radius: 2px; } QSlider::handle:horizontal { width: 16px; margin: -6px 0; background: @colorPrimary; border-radius: 8px; } QSlider::add-page:horizontal { background: @colorFillQuaternary; } QSlider::sub-page:horizontal { background: @colorPrimary; } /********************* 进度条 *********************/ QProgressBar { border: 1px solid @colorBorder; border-radius: @radius_base; background: @colorFillQuaternary; text-align: center; color: @colorText; padding: 1px; min-height: 10px; } QProgressBar::chunk { background: @colorPrimary; border-radius: calc(@radius_base - 1px); } /********************* 菜单控件 *********************/ QMenu { background: @colorBgContainer; border: 1px solid @colorBorder; padding: 4px; } QMenu::item { padding: 6px 24px; margin: 2px; border-radius: @radius_small; } QMenu::item:selected { background: @colorPrimaryBg; color: @colorPrimaryText; } QMenu::separator { height: 1px; background: @colorBorder; } /********************* 表格控件 *********************/ QHeaderView::section { background: @colorBgContainer; padding: 4px; border: 1px solid @colorBorder; } QTableView QTableCornerButton::section { background: @colorBgContainer; border: 1px solid @colorBorder; } /********************* 工具栏 *********************/ QToolBar { background: @colorBgContainer; border-bottom: 1px solid @colorBorder; spacing: 4px; padding: 4px; } QToolBar::separator { width: 1px; background: @colorBorder; margin: 0 4px; } /********************* 对话框 *********************/ QDialog { background: @colorBgContainer; border: 1px solid @colorBorder; border-radius: @radius_base; } QMessageBox QLabel#qt_msgbox_label { color: @colorText; padding: 12px; } QMessageBox QPushButton { min-width: 80px; margin: 4px; } /********************* 工具提示 *********************/ QToolTip { color: @colorText; background-color: @colorBgContainer; border: 1px solid @colorBorder; border-radius: @radius_small; padding: 4px 8px; } /********************* 特殊状态 *********************/ /* 错误状态 */ .error-border { border: 1px solid @colorError !important; } .error-text { color: @colorError !important; } /* 禁用状态 */ .disabled-text { color: @colorTextQuaternary; } .disabled-bg { background-color: @colorFillQuaternary; } /* 链接样式 */ QLabel[link="true"] { color: @colorLink; } QLabel[link="true"]:hover { color: @colorLinkHover; } /********************* 动态尺寸 *********************/ .small { font-size: 12px; padding: 4px; qproperty-iconSize: 16px; } .medium { font-size: 14px; padding: 8px; qproperty-iconSize: 20px; } .large { font-size: 16px; padding: 12px; qproperty-iconSize: 24px; } /********************* 工具按钮 *********************/ QToolButton { padding: 4px; border-radius: @radius_small; background: transparent; } QToolButton:hover { background: @colorPrimaryBgHover; } QToolButton:pressed { background: @colorPrimaryBg; } /********************* 分割线 *********************/ QSplitter::handle { background: @colorBorder; margin: 2px; } QSplitter::handle:hover { background: @colorPrimary; } /********************* 状态栏 *********************/ QStatusBar { background-color: @colorBgContainer; border-top: 1px solid @colorBorder; color: @colorText; min-height: 24px; padding: 2px 4px; } QStatusBar::item { border-right: 1px solid @colorBorder; margin: 0; } /********************* 状态类 *********************/ .active { background-color: @colorPrimaryActive; color: @colorPrimaryTextActive; } .hover { background-color: @colorPrimaryBgHover; color: @colorPrimaryTextHover; } )Raw"; namespace { const int hueStep = 2; // 色相阶梯 const double saturationStep = 0.16; // 饱和度阶梯,浅色部分 const double saturationStep2 = 0.05; // 饱和度阶梯,深色部分 const double brightnessStep1 = 0.05; // 亮度阶梯,浅色部分 const double brightnessStep2 = 0.15; // 亮度阶梯,深色部分 const int lightColorCount = 5; // 浅色数量,主色上 const int darkColorCount = 4; // 深色数量,主色下 // 暗色主题颜色映射关系表 const QVector darkColorMap = {7, 6, 5, 5, 5, 5, 4, 3, 2, 1}; const QVector darkMixRatios = {15, 25, 30, 45, 65, 85, 90, 95, 97, 98}; } // namespace ThemeManager &ThemeManager::instance() { static ThemeManager instance; return instance; } ThemeManager::ThemeManager(QObject *parent) : QObject(parent) { token.colorPrimary = QColor("#1677ff"); token.colorSuccess = QColor("#52c41a"); token.colorWarning = QColor("#faad14"); token.colorError = QColor("#ff4d4f"); token.colorInfo = QColor("#1677ff"); token.colorLink = QColor(""); token.colorTextBase = QColor(""); token.colorBgBase = QColor(""); token.fontFamily = QFont(); // Line token.lineWidth = 1; token.lineType = "solid"; // Radius token.borderRadius = 6; // Size token.sizeUnit = 4; token.sizeStep = 4; token.sizePopupArrow = 16; token.controlHeight = 32; QJsonDocument doc = QJsonDocument::fromJson(defaultConfig); loadConfig(doc.object()); } void ThemeManager::loadConfig(const QJsonObject &object) { m_colorConfig = object.value("color_mode").toObject(); m_sizeConfig = object.value("size_mode").toObject(); generateDerivedColors(); } void ThemeManager::loadConfig(const QString &configPath) { QFile configFile(configPath); if (!configFile.open(QIODevice::ReadOnly)) return; QJsonDocument doc = QJsonDocument::fromJson(configFile.readAll()); if (doc.isNull() || !doc.isObject()) { qWarning() << "Invalid JSON in config file:" << configPath; return; } loadConfig(doc.object()); } void ThemeManager::applyTheme() { QString qss = loadQssTemplate(); QString processed = processQss(qss); // qDebug().noquote() << "Processed QSS:" << processed; // 输出生成的 QSS qApp->setStyleSheet(processed); emit themeChanged(); } QColor ThemeManager::color(const QString &name) const { QMetaEnum metaEnm = QMetaEnum::fromType(); bool isok = false; int value = metaEnm.keyToValue(name.toUtf8().data(), &isok); if (isok) { return m_colorTokens.value((ColorToken) value, QColor(Qt::black)); } qDebug() << "no find " << name << m_colorTokens; return QColor(Qt::black); } QColor ThemeManager::color(ColorToken token) const { return m_colorTokens.value(token, QColor(Qt::black)); } void ThemeManager::setPrimaryColor(const QColor &color) { if (token.colorPrimary != color && color.isValid()) { token.colorPrimary = color; generateDerivedColors(); applyTheme(); } } void ThemeManager::toggleThemeMode() { m_themeMode = (m_themeMode == Light) ? Dark : Light; generateDerivedColors(); applyTheme(); } ThemeManager::ThemeMode ThemeManager::themeMode() const { return m_themeMode; } void ThemeManager::setThemeMode(ThemeMode themeMode) { if (m_themeMode != themeMode) { m_themeMode = themeMode; generateDerivedColors(); } applyTheme(); } ThemeManager::SizeMode ThemeManager::sizeMode() const { return m_sizeMode; } void ThemeManager::setSizeMode(SizeMode mode) { if (m_sizeMode != mode) { m_sizeMode = mode; applyTheme(); } } void ThemeManager::generateDerivedColors() { m_colors.clear(); auto generateColorPalettes = [this](const QColor baseColor) { if (!baseColor.isValid()) { return QHash(); } const QVector colors = generateAntdPalette(baseColor); return QHash{ {1, colors[0]}, // {2, colors[1]}, // {3, colors[2]}, // {4, colors[3]}, // {5, colors[4]}, // {6, colors[5]}, // {7, colors[6]}, // {8, colors[4]}, // {9, colors[5]}, // {10, colors[6]}, // //{8: colors[7]}, //{9: colors[8]}, //{10: colors[9]}; }; }; QString themeKey = (m_themeMode == Light) ? "light" : "dark"; QJsonObject themeColors = m_colorConfig.value(themeKey).toObject(); // 加载基础颜色 for (auto it = themeColors.begin(); it != themeColors.end(); ++it) { QColor color(it.value().toString()); if (color.isValid()) { m_colors[it.key()] = color; } } // 创建 类似 antd 的颜色配置 const auto primaryColors = generateColorPalettes(token.colorPrimary); const auto successColors = generateColorPalettes(token.colorSuccess); const auto warningColors = generateColorPalettes(token.colorWarning); const auto errorColors = generateColorPalettes(token.colorError); const auto infoColors = generateColorPalettes(token.colorInfo); generateNeutralColorPalettes(token.colorBgBase, token.colorTextBase); // Color Link auto colorLink = token.colorLink; if (!token.colorLink.isValid()) { colorLink = token.colorInfo; } const auto linkColors = generateColorPalettes(colorLink); const auto colorErrorBgFilledHover = mixColor(errorColors[1], errorColors[3], 50); m_colorTokens[ColorToken::colorPrimaryBg] = primaryColors[1]; m_colorTokens[ColorToken::colorPrimaryBgHover] = primaryColors[2]; m_colorTokens[ColorToken::colorPrimaryBorder] = primaryColors[3]; m_colorTokens[ColorToken::colorPrimaryBorderHover] = primaryColors[4]; m_colorTokens[ColorToken::colorPrimaryHover] = primaryColors[5]; m_colorTokens[ColorToken::colorPrimary] = primaryColors[6]; m_colorTokens[ColorToken::colorPrimaryActive] = primaryColors[7]; m_colorTokens[ColorToken::colorPrimaryTextHover] = primaryColors[8]; m_colorTokens[ColorToken::colorPrimaryText] = primaryColors[9]; m_colorTokens[ColorToken::colorPrimaryTextActive] = primaryColors[10]; m_colorTokens[ColorToken::colorSuccessBg] = successColors[1]; m_colorTokens[ColorToken::colorSuccessBgHover] = successColors[2]; m_colorTokens[ColorToken::colorSuccessBorder] = successColors[3]; m_colorTokens[ColorToken::colorSuccessBorderHover] = successColors[4]; m_colorTokens[ColorToken::colorSuccessHover] = successColors[4]; m_colorTokens[ColorToken::colorSuccess] = successColors[6]; m_colorTokens[ColorToken::colorSuccessActive] = successColors[7]; m_colorTokens[ColorToken::colorSuccessTextHover] = successColors[8]; m_colorTokens[ColorToken::colorSuccessText] = successColors[9]; m_colorTokens[ColorToken::colorSuccessTextActive] = successColors[10]; m_colorTokens[ColorToken::colorErrorBg] = errorColors[1]; m_colorTokens[ColorToken::colorErrorBgHover] = errorColors[2]; m_colorTokens[ColorToken::colorErrorBgFilledHover] = colorErrorBgFilledHover; m_colorTokens[ColorToken::colorErrorBgActive] = errorColors[3]; m_colorTokens[ColorToken::colorErrorBorder] = errorColors[3]; m_colorTokens[ColorToken::colorErrorBorderHover] = errorColors[4]; m_colorTokens[ColorToken::colorErrorHover] = errorColors[5]; m_colorTokens[ColorToken::colorError] = errorColors[6]; m_colorTokens[ColorToken::colorErrorActive] = errorColors[7]; m_colorTokens[ColorToken::colorErrorTextHover] = errorColors[8]; m_colorTokens[ColorToken::colorErrorText] = errorColors[9]; m_colorTokens[ColorToken::colorErrorTextActive] = errorColors[10]; m_colorTokens[ColorToken::colorWarningBg] = warningColors[1]; m_colorTokens[ColorToken::colorWarningBgHover] = warningColors[2]; m_colorTokens[ColorToken::colorWarningBorder] = warningColors[3]; m_colorTokens[ColorToken::colorWarningBorderHover] = warningColors[4]; m_colorTokens[ColorToken::colorWarningHover] = warningColors[4]; m_colorTokens[ColorToken::colorWarning] = warningColors[6]; m_colorTokens[ColorToken::colorWarningActive] = warningColors[7]; m_colorTokens[ColorToken::colorWarningTextHover] = warningColors[8]; m_colorTokens[ColorToken::colorWarningText] = warningColors[9]; m_colorTokens[ColorToken::colorWarningTextActive] = warningColors[10]; m_colorTokens[ColorToken::colorInfoBg] = infoColors[1]; m_colorTokens[ColorToken::colorInfoBgHover] = infoColors[2]; m_colorTokens[ColorToken::colorInfoBorder] = infoColors[3]; m_colorTokens[ColorToken::colorInfoBorderHover] = infoColors[4]; m_colorTokens[ColorToken::colorInfoHover] = infoColors[4]; m_colorTokens[ColorToken::colorInfo] = infoColors[6]; m_colorTokens[ColorToken::colorInfoActive] = infoColors[7]; m_colorTokens[ColorToken::colorInfoTextHover] = infoColors[8]; m_colorTokens[ColorToken::colorInfoText] = infoColors[9]; m_colorTokens[ColorToken::colorInfoTextActive] = infoColors[10]; m_colorTokens[ColorToken::colorLinkHover] = linkColors[4]; m_colorTokens[ColorToken::colorLink] = linkColors[6]; m_colorTokens[ColorToken::colorLinkActive] = linkColors[7]; m_colorTokens[ColorToken::colorWhite] = "#fff"; // init m_color QMetaEnum metaEnum = QMetaEnum::fromType(); for (auto it = m_colorTokens.constBegin(); it != m_colorTokens.constEnd(); ++it) { const char *enumKey = metaEnum.valueToKey(static_cast(it.key())); if (enumKey) { m_colors.insert(QString(enumKey), it.value()); } else { qWarning() << "Invalid ColorToken value:" << static_cast(it.key()); } } } QString ThemeManager::loadQssTemplate() const { return QString::fromUtf8(defaultQss); } QString ThemeManager::processQss(const QString &qss) const { QString processed = qss; // 第一步:替换尺寸变量(无依赖关系) QString sizeKey; switch (m_sizeMode) { case Small: sizeKey = "small"; break; case Medium: sizeKey = "medium"; break; case Large: sizeKey = "large"; break; } QJsonObject sizeValues = m_sizeConfig.value(sizeKey).toObject(); for (auto it = sizeValues.begin(); it != sizeValues.end(); ++it) { processed.replace("@" + it.key(), it.value().toString()); } // 第二步:使用正则表达式替换颜色变量(处理依赖关系) static const QRegularExpression varRegex("@([a-zA-Z0-9_]+)"); QRegularExpressionMatchIterator it = varRegex.globalMatch(processed); QSet replacedVars; while (it.hasNext()) { QRegularExpressionMatch match = it.next(); QString varName = match.captured(1); if (replacedVars.contains(varName)) continue; if (m_colors.contains(varName)) { QColor color = m_colors[varName]; QString colorStr = color.alpha() == 255 ? color.name() : QString("rgba(%1, %2, %3, %4)") .arg(color.red()) .arg(color.green()) .arg(color.blue()) .arg(color.alphaF()); // 使用单词边界确保精确匹配 processed.replace(QRegularExpression(QString("@%1\\b").arg(varName)), colorStr); replacedVars.insert(varName); } } return processed; } QVector ThemeManager::generateAntdPalette(const QColor &baseColor) const { QVector palette; QColor base = baseColor.toHsv(); qreal h, s, v; base.getHsvF(&h, &s, &v); h = qRound(h * 360); auto getHue = [](qreal hsv_h, int i, bool light) -> qreal { qreal hue; if (qRound(hsv_h) >= 60 && qRound(hsv_h) <= 240) { hue = light ? qRound(hsv_h) - hueStep * i : qRound(hsv_h) + hueStep * i; } else { hue = light ? qRound(hsv_h) + hueStep * i : qRound(hsv_h) - hueStep * i; } return std::fmod(hue + 360, 360); }; auto getSaturation = [](qreal hsv_s, int i, bool light) -> qreal { // 灰色不改变饱和度 if (qFuzzyIsNull(hsv_s)) { return hsv_s; } qreal saturation; if (light) { saturation = hsv_s - saturationStep * i; } else if (i == darkColorCount) { saturation = hsv_s + saturationStep; } else { saturation = hsv_s + saturationStep2 * i; } // 边界值修正 if (saturation > 1) { saturation = 1; } // 第一格的 s 限制在 0.06-0.1 之间 if (light && i == lightColorCount && saturation > 0.1) { saturation = 0.1; } if (saturation < 0.06) { saturation = 0.06; } return saturation; }; auto getValue = [](qreal hsv_v, int i, bool light) -> qreal { qreal value; if (light) { value = hsv_v + brightnessStep1 * i; } else { value = hsv_v - brightnessStep2 * i; } return qBound(0.0, value, 1.0); }; // 生成浅色系 for (int i = lightColorCount; i > 0; --i) { qreal newH = getHue(h, i, true); qreal newS = getSaturation(s, i, true); qreal newV = getValue(v, i, true); palette.append(QColor::fromHsvF(newH / 360.0, newS, newV)); } // 添加主色 palette.append(baseColor); // 生成深色系 for (int i = 1; i <= darkColorCount; ++i) { qreal newH = getHue(h, i, false); qreal newS = getSaturation(s, i, false); qreal newV = getValue(v, i, false); palette.append(QColor::fromHsvF(newH / 360.0, newS, newV)); } // 暗色主题处理 if (m_themeMode == Dark) { return applyDarkMap(palette); } return palette; } QVector ThemeManager::applyDarkMap(const QVector &palette, const QColor &backgroundColor) const { QVector darkPalette; QColor bgColor = backgroundColor; if (!bgColor.isValid()) { bgColor = QColor("#141414"); } for (int i = 0; i < palette.size(); ++i) { darkPalette.append(mixColor(bgColor, palette[darkColorMap[i]], darkMixRatios[i])); } return darkPalette; } void ThemeManager::generateNeutralColorPalettes(const QColor &baseColor, const QColor &textBaseColor) { QColor colorBgBase = baseColor; if (!colorBgBase.isValid()) { if (m_themeMode == Light) { colorBgBase = QColor("#FFFFFFFF"); } else { colorBgBase = QColor("#000"); } } QColor colorTextBase = textBaseColor; if (!colorTextBase.isValid()) { if (m_themeMode == Light) { colorTextBase = QColor("#000"); } else { colorTextBase = QColor("#FFFFFFFF"); } } m_colorTokens[ColorToken::colorBgBase] = colorBgBase; m_colorTokens[ColorToken::colorTextBase] = colorTextBase; auto getAlphaColor = [](const QColor &baseColor, qreal alpha) { QColor result = baseColor; result.setAlphaF(alpha); return result; }; auto getSolidColor = [this](const QColor &baseColor, int amount) { // if (m_themeMode == Dark) { // return baseColor.darker(amount); // } else { // return baseColor.lighter(amount); // } QColor hsl = baseColor.toHsl(); qreal h = hsl.hslHueF(); // 0.0 <= hueF < 360.0 qreal s = hsl.hslSaturationF(); qreal l = hsl.lightnessF(); if (m_themeMode == Dark) { l = l + amount / 100.0; if (l > 1) { l = 1; } } else { l = l - amount / 100.0; if (l < 0) { l = 0; } } return QColor::fromHslF(h, s, l, baseColor.alphaF()); }; if (m_themeMode == Dark) { m_colorTokens[ColorToken::colorText] = getAlphaColor(colorTextBase, 0.85); m_colorTokens[ColorToken::colorTextSecondary] = getAlphaColor(colorTextBase, 0.65); m_colorTokens[ColorToken::colorTextTertiary] = getAlphaColor(colorTextBase, 0.45); m_colorTokens[ColorToken::colorTextQuaternary] = getAlphaColor(colorTextBase, 0.25); m_colorTokens[ColorToken::colorFill] = getAlphaColor(colorTextBase, 0.18); m_colorTokens[ColorToken::colorFillSecondary] = getAlphaColor(colorTextBase, 0.12); m_colorTokens[ColorToken::colorFillTertiary] = getAlphaColor(colorTextBase, 0.08); m_colorTokens[ColorToken::colorFillQuaternary] = getAlphaColor(colorTextBase, 0.04); m_colorTokens[ColorToken::colorBgSolid] = getAlphaColor(colorTextBase, 0.95); m_colorTokens[ColorToken::colorBgSolidHover] = getAlphaColor(colorTextBase, 1); m_colorTokens[ColorToken::colorBgSolidActive] = getAlphaColor(colorTextBase, 0.9); m_colorTokens[ColorToken::colorBgElevated] = getSolidColor(colorBgBase, 12); m_colorTokens[ColorToken::colorBgContainer] = getSolidColor(colorBgBase, 8); m_colorTokens[ColorToken::colorBgLayout] = getSolidColor(colorBgBase, 0); m_colorTokens[ColorToken::colorBgSpotlight] = getSolidColor(colorBgBase, 26); m_colorTokens[ColorToken::colorBgBlur] = getAlphaColor(colorTextBase, 0.04); m_colorTokens[ColorToken::colorBorder] = getSolidColor(colorBgBase, 26); m_colorTokens[ColorToken::colorBorderSecondary] = getSolidColor(colorBgBase, 19); } else { m_colorTokens[ColorToken::colorText] = getAlphaColor(colorTextBase, 0.88); m_colorTokens[ColorToken::colorTextSecondary] = getAlphaColor(colorTextBase, 0.65); m_colorTokens[ColorToken::colorTextTertiary] = getAlphaColor(colorTextBase, 0.45); m_colorTokens[ColorToken::colorTextQuaternary] = getAlphaColor(colorTextBase, 0.25); m_colorTokens[ColorToken::colorFill] = getAlphaColor(colorTextBase, 0.15); m_colorTokens[ColorToken::colorFillSecondary] = getAlphaColor(colorTextBase, 0.06); m_colorTokens[ColorToken::colorFillTertiary] = getAlphaColor(colorTextBase, 0.04); m_colorTokens[ColorToken::colorFillQuaternary] = getAlphaColor(colorTextBase, 0.02); m_colorTokens[ColorToken::colorBgSolid] = getAlphaColor(colorTextBase, 1); m_colorTokens[ColorToken::colorBgSolidHover] = getAlphaColor(colorTextBase, 0.75); m_colorTokens[ColorToken::colorBgSolidActive] = getAlphaColor(colorTextBase, 0.95); m_colorTokens[ColorToken::colorBgLayout] = getSolidColor(colorBgBase, 4); m_colorTokens[ColorToken::colorBgContainer] = getSolidColor(colorBgBase, 0); m_colorTokens[ColorToken::colorBgElevated] = getSolidColor(colorBgBase, 0); m_colorTokens[ColorToken::colorBgSpotlight] = getAlphaColor(colorTextBase, 0.85); m_colorTokens[ColorToken::colorBgBlur] = "transparent"; m_colorTokens[ColorToken::colorBorder] = getSolidColor(colorBgBase, 15); m_colorTokens[ColorToken::colorBorderSecondary] = getSolidColor(colorBgBase, 6); } } QColor ThemeManager::mixColor(const QColor &base, const QColor &target, int ratio) const { const qreal p = ratio / 100.0; qreal r = (base.red() - target.red()) * p + target.red(); qreal g = (base.green() - target.green()) * p + target.green(); qreal b = (base.blue() - target.blue()) * p + target.blue(); return QColor(r, g, b); } QMap ThemeManager::genControlHeight(const SeedTokens &token) { const int controlHeight = token.controlHeight; return { {controlHeightSM, controlHeight * 0.75}, {controlHeightXS, controlHeight * 0.5}, {controlHeightLG, controlHeight * 1.25}, }; } QMap ThemeManager::genSizeMapToken(const SeedTokens &token) { const int sizeUnit = token.sizeUnit; const int sizeStep = token.sizeStep; const int compactSizeStep = sizeStep - 2; return { {SizeXXL, sizeUnit * (compactSizeStep + 10)}, {SizeXL, sizeUnit * (compactSizeStep + 6)}, {SizeLG, sizeUnit * (compactSizeStep + 2)}, {SizeMD, sizeUnit * (compactSizeStep + 2)}, {SizeMS, sizeUnit * (compactSizeStep + 1)}, {size, sizeUnit * compactSizeStep}, {SizeSM, sizeUnit * compactSizeStep}, {SizeXS, sizeUnit * (compactSizeStep - 1)}, {SizeXXS, sizeUnit * (compactSizeStep - 1)}, }; }