refactor/pure-qt-ui #3
|
|
@ -74,8 +74,8 @@ QWidget* buildAppearancePage() {
|
||||||
rlay->setContentsMargins(96 + 12, 0, 0, 0); // 与控件列对齐
|
rlay->setContentsMargins(96 + 12, 0, 0, 0); // 与控件列对齐
|
||||||
rlay->setSpacing(10);
|
rlay->setSpacing(10);
|
||||||
auto* hint = new QLabel(QStringLiteral("界面字号将在重启后生效"), restartRow);
|
auto* hint = new QLabel(QStringLiteral("界面字号将在重启后生效"), restartRow);
|
||||||
geopro::app::applyThemedStyleSheet(
|
geopro::app::applyTokenizedStyleSheet(
|
||||||
hint, QStringLiteral("color:#5A6B85; font-size:%1px;")
|
hint, QStringLiteral("color:{{text/secondary}}; font-size:%1px;")
|
||||||
.arg(geopro::app::scaledPx(geopro::app::type::kCaption)));
|
.arg(geopro::app::scaledPx(geopro::app::type::kCaption)));
|
||||||
auto* restartBtn = new QPushButton(QStringLiteral("立即重启"), restartRow);
|
auto* restartBtn = new QPushButton(QStringLiteral("立即重启"), restartRow);
|
||||||
rlay->addWidget(hint);
|
rlay->addWidget(hint);
|
||||||
|
|
|
||||||
|
|
@ -449,60 +449,6 @@ ads--CDockWidgetTab[activeTab="true"] QLabel {
|
||||||
}
|
}
|
||||||
)QSS";
|
)QSS";
|
||||||
|
|
||||||
// ── 主题桥配色:工作台标准控件的 QSS/调色板颜色直接取自 ElaTheme,
|
|
||||||
// 保证里外(ElaWindow 外壳 ↔ 内部工作台)在明/暗两模下完全一致——这是关键,
|
|
||||||
// 否则外壳一种灰、工作台另一种灰,明暗都显得割裂。
|
|
||||||
// 做法:把浅色设计稿里每个色令牌按语义角色替换为 ElaTheme 当前模式的真实颜色。
|
|
||||||
// 浅→暗 颜色映射(全 UI 唯一颜色来源)。明色 = 令牌本身(QSS 直接写的就是明色);
|
|
||||||
// 暗色 = 此表对应值。覆盖全部 QSS 用色,缺一即在暗色下露浅色。改色只改这一处。
|
|
||||||
struct DarkPair {
|
|
||||||
const char* light;
|
|
||||||
const char* dark;
|
|
||||||
};
|
|
||||||
const DarkPair kDarkMap[] = {
|
|
||||||
// 背景(外壳→面板→抬升)
|
|
||||||
{"#F4F6FA", "#1E1F22"}, {"#FFFFFF", "#2B2D30"}, {"#EDF1F7", "#34373C"},
|
|
||||||
{"#F0F2F6", "#2B2D30"}, {"#EAEEF4", "#3A3D42"}, {"#EAEEF5", "#34373C"},
|
|
||||||
{"#EEF2FB", "#34373C"}, {"#E6EBF3", "#34373C"}, {"#EEF3FB", "#34373C"},
|
|
||||||
{"#EAF1FB", "#2F4257"}, {"#DCE6F4", "#2A3A4F"}, {"#DCE9F8", "#33527A"},
|
|
||||||
{"#FBEAD2", "#46371F"},
|
|
||||||
// 文字
|
|
||||||
{"#1F2A3D", "#E6E8EB"}, {"#5A6B85", "#A0A8B4"}, {"#3A475C", "#C4CCD8"},
|
|
||||||
{"#8A93A3", "#828B98"}, {"#9AA6B6", "#6E7681"}, {"#1B3D67", "#E8F1FB"},
|
|
||||||
// 边框
|
|
||||||
{"#D5DBE5", "#3A3D42"}, {"#C2CCDA", "#484C52"}, {"#C7D2E0", "#484C52"},
|
|
||||||
{"#E1E6EE", "#34373C"}, {"#E6EAF1", "#34373C"}, {"#EEF1F5", "#34373C"},
|
|
||||||
{"#DCE0E7", "#3A3D42"}, {"#A7B4C7", "#4A4E54"},
|
|
||||||
// 强调(品牌蓝)
|
|
||||||
{"#2D6CB5", "#5E9BD6"}, {"#2862A6", "#6FA8DD"}, {"#234F87", "#4E89C4"},
|
|
||||||
// 语义
|
|
||||||
{"#C0392B", "#E06A5E"}, {"#B45309", "#E0964A"}, {"#15803D", "#5BBF7A"},
|
|
||||||
};
|
|
||||||
|
|
||||||
QString darkOf(const QString& lightHex)
|
|
||||||
{
|
|
||||||
for (const auto& p : kDarkMap)
|
|
||||||
if (lightHex.compare(QLatin1String(p.light), Qt::CaseInsensitive) == 0)
|
|
||||||
return QString::fromLatin1(p.dark);
|
|
||||||
return lightHex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设计令牌(浅色 hex) → 当前明暗的真实色。
|
|
||||||
QColor roleColor(bool dark, const char* lightHex)
|
|
||||||
{
|
|
||||||
return dark ? QColor(darkOf(QString::fromLatin1(lightHex))) : QColor(QLatin1String(lightHex));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 把一段浅色设计稿 QSS 按当前明暗着色:明色原样;暗色把每个浅色令牌替换为暗色。
|
|
||||||
QString themedQss(const QString& designQss, bool dark)
|
|
||||||
{
|
|
||||||
if (!dark) return designQss;
|
|
||||||
QString s = designQss;
|
|
||||||
for (const auto& p : kDarkMap)
|
|
||||||
s.replace(QString::fromLatin1(p.light), QString::fromLatin1(p.dark), Qt::CaseInsensitive);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前模式的全局 QSS。
|
// 当前模式的全局 QSS。
|
||||||
QString styleSheetForMode(bool /*dark*/)
|
QString styleSheetForMode(bool /*dark*/)
|
||||||
{
|
{
|
||||||
|
|
@ -662,11 +608,6 @@ bool isDarkTheme()
|
||||||
return ThemeManager::instance().isDark();
|
return ThemeManager::instance().isDark();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString themed(const QString& designQss)
|
|
||||||
{
|
|
||||||
return themedQss(designQss, isDarkTheme());
|
|
||||||
}
|
|
||||||
|
|
||||||
void vtkBackground(double& r, double& g, double& b)
|
void vtkBackground(double& r, double& g, double& b)
|
||||||
{
|
{
|
||||||
// 规范 §0.5/§11:数据画布永远深色,不随明暗切换。取 canvas/bg。
|
// 规范 §0.5/§11:数据画布永远深色,不随明暗切换。取 canvas/bg。
|
||||||
|
|
@ -676,14 +617,6 @@ void vtkBackground(double& r, double& g, double& b)
|
||||||
b = c.blueF();
|
b = c.blueF();
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyThemedStyleSheet(QWidget* w, const QString& designQss)
|
|
||||||
{
|
|
||||||
if (!w) return;
|
|
||||||
w->setStyleSheet(themedQss(designQss, isDarkTheme()));
|
|
||||||
QObject::connect(&ThemeManager::instance(), &ThemeManager::changed, w,
|
|
||||||
[w, designQss]() { w->setStyleSheet(themedQss(designQss, isDarkTheme())); });
|
|
||||||
}
|
|
||||||
|
|
||||||
QString token(const char* name) { return tokenHex(name, isDarkTheme()); }
|
QString token(const char* name) { return tokenHex(name, isDarkTheme()); }
|
||||||
|
|
||||||
QColor tokenColor(const char* name) { return QColor(token(name)); }
|
QColor tokenColor(const char* name) { return QColor(token(name)); }
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class QWidget;
|
||||||
namespace geopro::app {
|
namespace geopro::app {
|
||||||
|
|
||||||
// 主题管理器(纯 Qt,替代 ElaTheme):持有当前明暗 + 是否跟随系统;切换发 changed() 信号,
|
// 主题管理器(纯 Qt,替代 ElaTheme):持有当前明暗 + 是否跟随系统;切换发 changed() 信号,
|
||||||
// 全 UI(全局 QSS 由 main 重应用、内联 chrome 由 applyThemedStyleSheet)据此热切重着色。
|
// 全 UI(全局 QSS 由 main 重应用、内联 chrome 由 applyTokenizedStyleSheet)据此热切重着色。
|
||||||
class ThemeManager : public QObject {
|
class ThemeManager : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
@ -103,7 +103,7 @@ inline constexpr const char* kWarningFill = "#FBEAD2"; // 警告底纹(配 kW
|
||||||
} // namespace semantic
|
} // namespace semantic
|
||||||
|
|
||||||
// 应用专业主题(Fusion + 调色板 + 全局样式表)。dark=true 走暗色(P2 主题桥用)。
|
// 应用专业主题(Fusion + 调色板 + 全局样式表)。dark=true 走暗色(P2 主题桥用)。
|
||||||
// 暗色复用同一 QSS 结构,仅按 kDarkMap 换色;幂等,可随主题切换重复调用。
|
// 暗色复用同一 QSS 结构,颜色全由 kTokens 双值(fillTokens/tokenHex)驱动;幂等,可随主题切换重复调用。
|
||||||
void applyThemeMode(QApplication& app, bool dark);
|
void applyThemeMode(QApplication& app, bool dark);
|
||||||
|
|
||||||
// 浅色主题快捷入口(= applyThemeMode(app,false))。经典壳启动调用一次。
|
// 浅色主题快捷入口(= applyThemeMode(app,false))。经典壳启动调用一次。
|
||||||
|
|
@ -128,15 +128,6 @@ bool isDarkTheme();
|
||||||
// VTK 渲染器背景色(随当前主题,取 ElaTheme 窗口底色)。写入 r/g/b(0–1)。
|
// VTK 渲染器背景色(随当前主题,取 ElaTheme 窗口底色)。写入 r/g/b(0–1)。
|
||||||
void vtkBackground(double& r, double& g, double& b);
|
void vtkBackground(double& r, double& g, double& b);
|
||||||
|
|
||||||
// 把一段「浅色设计稿 QSS」按当前 ElaTheme 配色着色应用到 widget,并随明/暗切换自动重着色。
|
|
||||||
// 用于 TopBar/PanelHeader/浮层 等带内联 setStyleSheet 的自定义 chrome——让它们也跟随主题
|
|
||||||
// (设计稿里用浅色令牌 #1F2A3D/#FFFFFF/#2D6CB5… 书写即可,与全局 QSS 同一套角色映射)。
|
|
||||||
void applyThemedStyleSheet(QWidget* w, const QString& designQss);
|
|
||||||
|
|
||||||
// 把一段「浅色设计稿 QSS」按当前 ElaTheme 配色着色后返回(供需要拼接/手动 setStyleSheet 的场景,
|
|
||||||
// 如 ADS dockManager 在其自带样式后追加规则)。不自动随主题切换,调用方需自行在切换时重取。
|
|
||||||
QString themed(const QString& designQss);
|
|
||||||
|
|
||||||
// ── 语义令牌(单一事实来源,取值见 Theme.cpp kTokens;规范 §1.5 + 附录 A + §1.3)──
|
// ── 语义令牌(单一事实来源,取值见 Theme.cpp kTokens;规范 §1.5 + 附录 A + §1.3)──
|
||||||
// 组件只引语义 token,禁止散落硬编码 hex。token 名形如 "bg/panel"、"accent/primary"。
|
// 组件只引语义 token,禁止散落硬编码 hex。token 名形如 "bg/panel"、"accent/primary"。
|
||||||
QString token(const char* name); // 当前明暗下的 hex(未知名返回品红 "#FF00FF" 以便一眼发现漏配)
|
QString token(const char* name); // 当前明暗下的 hex(未知名返回品红 "#FF00FF" 以便一眼发现漏配)
|
||||||
|
|
|
||||||
|
|
@ -89,17 +89,17 @@ LoginWindow::LoginWindow(geopro::net::AuthService& auth, QWidget* parent)
|
||||||
// 字号引用 Theme 排版令牌:品牌名=display(24)、副标题/字段标签=caption(12)。
|
// 字号引用 Theme 排版令牌:品牌名=display(24)、副标题/字段标签=caption(12)。
|
||||||
// 登录窗整体随 ElaTheme 着色(与 Ela 化的输入/按钮一致,避免暗系统下浅窗+暗控件割裂)。
|
// 登录窗整体随 ElaTheme 着色(与 Ela 化的输入/按钮一致,避免暗系统下浅窗+暗控件割裂)。
|
||||||
// 品牌带文字用 white 关键字(不入角色映射→恒为白),保证落在蓝色横幅上始终可读。
|
// 品牌带文字用 white 关键字(不入角色映射→恒为白),保证落在蓝色横幅上始终可读。
|
||||||
geopro::app::applyThemedStyleSheet(
|
geopro::app::applyTokenizedStyleSheet(
|
||||||
this, QStringLiteral(
|
this, QStringLiteral(
|
||||||
"QDialog { background: #F4F6FA; }"
|
"QDialog { background: {{bg/app}}; }"
|
||||||
"#headerBand {"
|
"#headerBand {"
|
||||||
" background: qlineargradient(x1:0, y1:0, x2:1, y2:1,"
|
" background: qlineargradient(x1:0, y1:0, x2:1, y2:1,"
|
||||||
" stop:0 #2D6CB5, stop:1 #234F87); }"
|
" stop:0 {{accent/primary}}, stop:1 {{accent/primary-pressed}}); }"
|
||||||
"#brandTitle { color: white; font-size: %1px; font-weight: %2; }"
|
"#brandTitle { color: {{text/on-primary}}; font-size: %1px; font-weight: %2; }"
|
||||||
"#brandSubtitle { color: rgba(255,255,255,0.82); font-size: %3px; }"
|
"#brandSubtitle { color: rgba(255,255,255,0.82); font-size: %3px; }"
|
||||||
"#fieldLabel { color: #5A6B85; font-size: %4px; font-weight: %5; }"
|
"#fieldLabel { color: {{text/secondary}}; font-size: %4px; font-weight: %5; }"
|
||||||
// 输入框已 Ela 化(ElaLineEdit 自绘 Fluent + 自动明暗),不再写 QLineEdit QSS。
|
// 输入框已 Ela 化(ElaLineEdit 自绘 Fluent + 自动明暗),不再写 QLineEdit QSS。
|
||||||
"#captchaImg { border: 1px solid #C7D2E0; border-radius: 8px; background: #EEF2FB; }")
|
"#captchaImg { border: 1px solid {{border/strong}}; border-radius: 8px; background: {{bg/hover}}; }")
|
||||||
.arg(scaledPx(type::kDisplay))
|
.arg(scaledPx(type::kDisplay))
|
||||||
.arg(type::kWeightBold)
|
.arg(type::kWeightBold)
|
||||||
.arg(scaledPx(type::kCaption))
|
.arg(scaledPx(type::kCaption))
|
||||||
|
|
@ -178,11 +178,11 @@ LoginWindow::LoginWindow(geopro::net::AuthService& auth, QWidget* parent)
|
||||||
refreshBtn_ = new QPushButton(QStringLiteral("看不清?换一张"), body);
|
refreshBtn_ = new QPushButton(QStringLiteral("看不清?换一张"), body);
|
||||||
refreshBtn_->setFlat(true);
|
refreshBtn_->setFlat(true);
|
||||||
refreshBtn_->setCursor(Qt::PointingHandCursor);
|
refreshBtn_->setCursor(Qt::PointingHandCursor);
|
||||||
geopro::app::applyThemedStyleSheet(
|
geopro::app::applyTokenizedStyleSheet(
|
||||||
refreshBtn_,
|
refreshBtn_,
|
||||||
QStringLiteral(
|
QStringLiteral(
|
||||||
"QPushButton { color: #2D6CB5; border: none; background: transparent; padding: 2px 0; }"
|
"QPushButton { color: {{accent/primary}}; border: none; background: transparent; padding: 2px 0; }"
|
||||||
"QPushButton:hover { color: #234F87; text-decoration: underline; }"));
|
"QPushButton:hover { color: {{accent/primary-pressed}}; text-decoration: underline; }"));
|
||||||
refreshRow->addWidget(refreshBtn_);
|
refreshRow->addWidget(refreshBtn_);
|
||||||
form->addLayout(refreshRow);
|
form->addLayout(refreshRow);
|
||||||
|
|
||||||
|
|
@ -193,8 +193,8 @@ LoginWindow::LoginWindow(geopro::net::AuthService& auth, QWidget* parent)
|
||||||
|
|
||||||
// 错误提示:固定占位高度,避免出现时整体布局跳动。
|
// 错误提示:固定占位高度,避免出现时整体布局跳动。
|
||||||
errorLabel_ = new QLabel(body);
|
errorLabel_ = new QLabel(body);
|
||||||
geopro::app::applyThemedStyleSheet(
|
geopro::app::applyTokenizedStyleSheet(
|
||||||
errorLabel_, QStringLiteral("color: #C0392B; font-size: %1px;").arg(scaledPx(type::kCaption)));
|
errorLabel_, QStringLiteral("color: {{status/danger}}; font-size: %1px;").arg(scaledPx(type::kCaption)));
|
||||||
errorLabel_->setWordWrap(true);
|
errorLabel_->setWordWrap(true);
|
||||||
errorLabel_->setMinimumHeight(18);
|
errorLabel_->setMinimumHeight(18);
|
||||||
form->addWidget(errorLabel_);
|
form->addWidget(errorLabel_);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue