fix(ela): 登录窗主题化 + VTK 背景稳健随主题 + 扫清所有硬编码浅色 QSS 遗漏
- 登录窗: setStyleSheet → applyThemedStyleSheet(整窗随主题, 品牌带文字用 white 关键字恒白); refreshBtn/errorLabel 也随主题。修暗系统下浅窗+暗 Ela 控件割裂、占位文字看不清 - VTK: 改为主题切换时重跑 rebuildCentral/rebuildDetail(走完整渲染必重绘, 兼顾 syncSystemTheme 异步切暗时序); rebuildDetail 也按 vtkBackground 设背景 - 主动扫描修掉遗漏: dockManager 分隔条、数据集/文件列表、对象树枝/hint、空状态标题/提示 全走主题 - themed() 公共助手(给需拼接 ADS 样式的 dockManager)
This commit is contained in:
parent
f5eff9e185
commit
68d832c57b
|
|
@ -467,6 +467,11 @@ bool isDarkTheme()
|
||||||
return eTheme->getThemeMode() == ElaThemeType::Dark;
|
return eTheme->getThemeMode() == ElaThemeType::Dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
const QColor c = roleColor(isDarkTheme(), ElaThemeType::WindowBase);
|
const QColor c = roleColor(isDarkTheme(), ElaThemeType::WindowBase);
|
||||||
|
|
|
||||||
|
|
@ -101,4 +101,8 @@ void vtkBackground(double& r, double& g, double& b);
|
||||||
// (设计稿里用浅色令牌 #1F2A3D/#FFFFFF/#2D6CB5… 书写即可,与全局 QSS 同一套角色映射)。
|
// (设计稿里用浅色令牌 #1F2A3D/#FFFFFF/#2D6CB5… 书写即可,与全局 QSS 同一套角色映射)。
|
||||||
void applyThemedStyleSheet(QWidget* w, const QString& designQss);
|
void applyThemedStyleSheet(QWidget* w, const QString& designQss);
|
||||||
|
|
||||||
|
// 把一段「浅色设计稿 QSS」按当前 ElaTheme 配色着色后返回(供需要拼接/手动 setStyleSheet 的场景,
|
||||||
|
// 如 ADS dockManager 在其自带样式后追加规则)。不自动随主题切换,调用方需自行在切换时重取。
|
||||||
|
QString themed(const QString& designQss);
|
||||||
|
|
||||||
} // namespace geopro::app
|
} // namespace geopro::app
|
||||||
|
|
|
||||||
|
|
@ -91,21 +91,24 @@ LoginWindow::LoginWindow(geopro::net::AuthService& auth, QWidget* parent)
|
||||||
// 仅外观:登录窗自带样式(沿用全局主题令牌,保证一脉相承)。
|
// 仅外观:登录窗自带样式(沿用全局主题令牌,保证一脉相承)。
|
||||||
// QLineEdit 在所有状态都显式白底深字 + 边框,避免失焦时取调色板默认色与背景相近不可读。
|
// QLineEdit 在所有状态都显式白底深字 + 边框,避免失焦时取调色板默认色与背景相近不可读。
|
||||||
// 字号引用 Theme 排版令牌:品牌名=display(24)、副标题/字段标签=caption(12)。
|
// 字号引用 Theme 排版令牌:品牌名=display(24)、副标题/字段标签=caption(12)。
|
||||||
setStyleSheet(QStringLiteral(
|
// 登录窗整体随 ElaTheme 着色(与 Ela 化的输入/按钮一致,避免暗系统下浅窗+暗控件割裂)。
|
||||||
"QDialog { background: #F4F6FA; }"
|
// 品牌带文字用 white 关键字(不入角色映射→恒为白),保证落在蓝色横幅上始终可读。
|
||||||
"#headerBand {"
|
geopro::app::applyThemedStyleSheet(
|
||||||
" background: qlineargradient(x1:0, y1:0, x2:1, y2:1,"
|
this, QStringLiteral(
|
||||||
" stop:0 #2D6CB5, stop:1 #234F87); }"
|
"QDialog { background: #F4F6FA; }"
|
||||||
"#brandTitle { color: #FFFFFF; font-size: %1px; font-weight: %2; }"
|
"#headerBand {"
|
||||||
"#brandSubtitle { color: rgba(255,255,255,0.82); font-size: %3px; }"
|
" background: qlineargradient(x1:0, y1:0, x2:1, y2:1,"
|
||||||
"#fieldLabel { color: #5A6B85; font-size: %4px; font-weight: %5; }"
|
" stop:0 #2D6CB5, stop:1 #234F87); }"
|
||||||
// 输入框已 Ela 化(ElaLineEdit 自绘 Fluent + 自动明暗),不再写 QLineEdit QSS。
|
"#brandTitle { color: white; font-size: %1px; font-weight: %2; }"
|
||||||
"#captchaImg { border: 1px solid #C7D2E0; border-radius: 8px; background: #EEF2FB; }")
|
"#brandSubtitle { color: rgba(255,255,255,0.82); font-size: %3px; }"
|
||||||
.arg(type::kDisplay)
|
"#fieldLabel { color: #5A6B85; font-size: %4px; font-weight: %5; }"
|
||||||
.arg(type::kWeightBold)
|
// 输入框已 Ela 化(ElaLineEdit 自绘 Fluent + 自动明暗),不再写 QLineEdit QSS。
|
||||||
.arg(type::kCaption)
|
"#captchaImg { border: 1px solid #C7D2E0; border-radius: 8px; background: #EEF2FB; }")
|
||||||
.arg(type::kCaption)
|
.arg(type::kDisplay)
|
||||||
.arg(type::kWeightSemibold));
|
.arg(type::kWeightBold)
|
||||||
|
.arg(type::kCaption)
|
||||||
|
.arg(type::kCaption)
|
||||||
|
.arg(type::kWeightSemibold));
|
||||||
|
|
||||||
auto* root = new QVBoxLayout(this);
|
auto* root = new QVBoxLayout(this);
|
||||||
root->setContentsMargins(0, 0, 0, 0);
|
root->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
@ -179,9 +182,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);
|
||||||
refreshBtn_->setStyleSheet(QStringLiteral(
|
geopro::app::applyThemedStyleSheet(
|
||||||
"QPushButton { color: #2D6CB5; border: none; background: transparent; padding: 2px 0; }"
|
refreshBtn_,
|
||||||
"QPushButton:hover { color: #234F87; text-decoration: underline; }"));
|
QStringLiteral(
|
||||||
|
"QPushButton { color: #2D6CB5; border: none; background: transparent; padding: 2px 0; }"
|
||||||
|
"QPushButton:hover { color: #234F87; text-decoration: underline; }"));
|
||||||
refreshRow->addWidget(refreshBtn_);
|
refreshRow->addWidget(refreshBtn_);
|
||||||
form->addLayout(refreshRow);
|
form->addLayout(refreshRow);
|
||||||
|
|
||||||
|
|
@ -192,8 +197,8 @@ LoginWindow::LoginWindow(geopro::net::AuthService& auth, QWidget* parent)
|
||||||
|
|
||||||
// 错误提示:固定占位高度,避免出现时整体布局跳动。
|
// 错误提示:固定占位高度,避免出现时整体布局跳动。
|
||||||
errorLabel_ = new QLabel(body);
|
errorLabel_ = new QLabel(body);
|
||||||
errorLabel_->setStyleSheet(
|
geopro::app::applyThemedStyleSheet(
|
||||||
QStringLiteral("color: #C0392B; font-size: %1px;").arg(type::kCaption));
|
errorLabel_, QStringLiteral("color: #C0392B; font-size: %1px;").arg(type::kCaption));
|
||||||
errorLabel_->setWordWrap(true);
|
errorLabel_->setWordWrap(true);
|
||||||
errorLabel_->setMinimumHeight(18);
|
errorLabel_->setMinimumHeight(18);
|
||||||
form->addWidget(errorLabel_);
|
form->addWidget(errorLabel_);
|
||||||
|
|
|
||||||
|
|
@ -289,12 +289,19 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
window.setCentralWidget(dockManager);
|
window.setCentralWidget(dockManager);
|
||||||
|
|
||||||
// 覆盖 ADS 自带分隔条样式:其 default.css 用 palette(dark) 把面板间分隔画成深灰粗线,
|
// 覆盖 ADS 自带分隔条样式:其 default.css 用 palette(dark) 把面板间分隔画成深灰粗线,
|
||||||
// 且设在管理器自身、选择器更具体(优先级高于全局主题)。这里在其后追加同选择器规则覆盖为极淡分隔。
|
// 且设在管理器自身、选择器更具体(优先级高于全局主题)。在其后追加同选择器、按主题着色的极淡分隔覆盖。
|
||||||
dockManager->setStyleSheet(
|
// 捕获 ADS 基样式一次(避免每次切换重复追加而无限增长),切主题时用 base + 重新着色的覆盖。
|
||||||
dockManager->styleSheet() +
|
const QString dockBaseQss = dockManager->styleSheet();
|
||||||
QStringLiteral(
|
auto applyDockSplitter = [dockManager, dockBaseQss]() {
|
||||||
"ads--CDockContainerWidget ads--CDockSplitter::handle { background: #EAEEF4; }"
|
dockManager->setStyleSheet(
|
||||||
"ads--CDockContainerWidget ads--CDockSplitter::handle:hover { background: #C7D2E0; }"));
|
dockBaseQss +
|
||||||
|
geopro::app::themed(QStringLiteral(
|
||||||
|
"ads--CDockContainerWidget ads--CDockSplitter::handle { background: #EAEEF4; }"
|
||||||
|
"ads--CDockContainerWidget ads--CDockSplitter::handle:hover { background: #C7D2E0; }")));
|
||||||
|
};
|
||||||
|
applyDockSplitter();
|
||||||
|
QObject::connect(eTheme, &ElaTheme::themeModeChanged, dockManager,
|
||||||
|
[applyDockSplitter](ElaThemeType::ThemeMode) { applyDockSplitter(); });
|
||||||
|
|
||||||
// 面板包装:内容顶部加自绘表头(图标+标题+操作按钮),ADS 自带标题栏随后隐藏,
|
// 面板包装:内容顶部加自绘表头(图标+标题+操作按钮),ADS 自带标题栏随后隐藏,
|
||||||
// 从而让面板表头与原型完全一致。返回 [表头 + 内容] 容器供 setWidget。
|
// 从而让面板表头与原型完全一致。返回 [表头 + 内容] 容器供 setWidget。
|
||||||
|
|
@ -397,16 +404,17 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
|
|
||||||
auto* esTitle = new QLabel(QStringLiteral("选择左侧数据集开始分析"), emptyState);
|
auto* esTitle = new QLabel(QStringLiteral("选择左侧数据集开始分析"), emptyState);
|
||||||
esTitle->setAlignment(Qt::AlignCenter);
|
esTitle->setAlignment(Qt::AlignCenter);
|
||||||
esTitle->setStyleSheet(QStringLiteral("color:#5A6B85; font-size:%1px; font-weight:%2;")
|
geopro::app::applyThemedStyleSheet(
|
||||||
.arg(geopro::app::type::kHeading)
|
esTitle, QStringLiteral("color:#5A6B85; font-size:%1px; font-weight:%2;")
|
||||||
.arg(geopro::app::type::kWeightSemibold));
|
.arg(geopro::app::type::kHeading)
|
||||||
|
.arg(geopro::app::type::kWeightSemibold));
|
||||||
|
|
||||||
auto* esHint = new QLabel(QStringLiteral("单击左侧采集批次,查看反演剖面与异常点\n"
|
auto* esHint = new QLabel(QStringLiteral("单击左侧采集批次,查看反演剖面与异常点\n"
|
||||||
"切到「三维视图」可叠加帘面、体素与地形图层"),
|
"切到「三维视图」可叠加帘面、体素与地形图层"),
|
||||||
emptyState);
|
emptyState);
|
||||||
esHint->setAlignment(Qt::AlignCenter);
|
esHint->setAlignment(Qt::AlignCenter);
|
||||||
esHint->setStyleSheet(
|
geopro::app::applyThemedStyleSheet(
|
||||||
QStringLiteral("color:#8A93A3; font-size:%1px;").arg(geopro::app::type::kBody));
|
esHint, QStringLiteral("color:#8A93A3; font-size:%1px;").arg(geopro::app::type::kBody));
|
||||||
|
|
||||||
esLay->addWidget(esIcon);
|
esLay->addWidget(esIcon);
|
||||||
esLay->addWidget(esTitle);
|
esLay->addWidget(esTitle);
|
||||||
|
|
@ -443,17 +451,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
detailRenderWindow->AddRenderer(detailRenderer);
|
detailRenderWindow->AddRenderer(detailRenderer);
|
||||||
vtkRenderer* detailRendererPtr = detailRenderer.Get();
|
vtkRenderer* detailRendererPtr = detailRenderer.Get();
|
||||||
vtkGenericOpenGLRenderWindow* detailRenderWindowPtr = detailRenderWindow.Get();
|
vtkGenericOpenGLRenderWindow* detailRenderWindowPtr = detailRenderWindow.Get();
|
||||||
|
// 注:VTK 背景随主题切换的连接放在 rebuildCentral/rebuildDetail 定义之后(直接重跑它们,
|
||||||
// VTK 背景随主题切换:ElaTheme 明/暗切换时,中央+详情渲染器重设底色并刷新。
|
// 走完整渲染路径必重绘,比手动 SetBackground+Render 稳)。
|
||||||
QObject::connect(eTheme, &ElaTheme::themeModeChanged, detailWidget,
|
|
||||||
[rendererPtr, renderWindowPtr, detailRendererPtr, detailRenderWindowPtr]() {
|
|
||||||
double r, g, b;
|
|
||||||
geopro::app::vtkBackground(r, g, b);
|
|
||||||
rendererPtr->SetBackground(r, g, b);
|
|
||||||
renderWindowPtr->Render();
|
|
||||||
detailRendererPtr->SetBackground(r, g, b);
|
|
||||||
detailRenderWindowPtr->Render();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 数据详情容器:顶部「反演剖面/原数据」工具条 + 下方 QVTK 小视图。
|
// 数据详情容器:顶部「反演剖面/原数据」工具条 + 下方 QVTK 小视图。
|
||||||
auto* detailContainer = new QWidget();
|
auto* detailContainer = new QWidget();
|
||||||
|
|
@ -504,15 +503,16 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
// 左下 dock:数据真实显示栏(选中测线后列其采集批次=数据集;tab 数据/文件)。
|
// 左下 dock:数据真实显示栏(选中测线后列其采集批次=数据集;tab 数据/文件)。
|
||||||
auto* datasetTabs = new QTabWidget();
|
auto* datasetTabs = new QTabWidget();
|
||||||
auto* datasetList = new QListWidget();
|
auto* datasetList = new QListWidget();
|
||||||
// 简洁分割:去隔行变色,改为 item 间极淡分割线 + 内边距 + hover/选中反馈(专业、不误导)。
|
// 简洁分割:去隔行变色,改为 item 间极淡分割线 + 内边距 + hover/选中反馈(专业、不误导)。随主题着色。
|
||||||
datasetList->setStyleSheet(QStringLiteral(
|
const QString kListQss = QStringLiteral(
|
||||||
"QListWidget{ background:#FFFFFF; border:none; outline:none; }"
|
"QListWidget{ background:#FFFFFF; border:none; outline:none; }"
|
||||||
"QListWidget::item{ padding:9px 12px; border-bottom:1px solid #EEF1F5; color:#1F2A3D; }"
|
"QListWidget::item{ padding:9px 12px; border-bottom:1px solid #EEF1F5; color:#1F2A3D; }"
|
||||||
"QListWidget::item:hover{ background:#F5F8FD; }"
|
"QListWidget::item:hover{ background:#EEF3FB; }"
|
||||||
"QListWidget::item:selected{ background:#EAF1FB; color:#1F2A3D; }"));
|
"QListWidget::item:selected{ background:#EAF1FB; color:#1F2A3D; }");
|
||||||
|
geopro::app::applyThemedStyleSheet(datasetList, kListQss);
|
||||||
datasetTabs->addTab(datasetList, QStringLiteral("数据"));
|
datasetTabs->addTab(datasetList, QStringLiteral("数据"));
|
||||||
auto* fileList = new QListWidget();
|
auto* fileList = new QListWidget();
|
||||||
fileList->setStyleSheet(datasetList->styleSheet()); // 与数据页签同款简洁分割
|
geopro::app::applyThemedStyleSheet(fileList, kListQss); // 与数据页签同款简洁分割
|
||||||
datasetTabs->addTab(fileList, QStringLiteral("文件"));
|
datasetTabs->addTab(fileList, QStringLiteral("文件"));
|
||||||
auto* datasetDock = new ads::CDockWidget(QStringLiteral("数据真实显示栏"));
|
auto* datasetDock = new ads::CDockWidget(QStringLiteral("数据真实显示栏"));
|
||||||
auto* datasetBox = wrapWithHeader(
|
auto* datasetBox = wrapWithHeader(
|
||||||
|
|
@ -608,6 +608,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
fromCam->DeepCopy(detailRendererPtr->GetActiveCamera());
|
fromCam->DeepCopy(detailRendererPtr->GetActiveCamera());
|
||||||
|
|
||||||
detailRendererPtr->RemoveAllViewProps();
|
detailRendererPtr->RemoveAllViewProps();
|
||||||
|
{ // 背景随主题
|
||||||
|
double r, g, b;
|
||||||
|
geopro::app::vtkBackground(r, g, b);
|
||||||
|
detailRendererPtr->SetBackground(r, g, b);
|
||||||
|
}
|
||||||
if (currentDsId->isEmpty()) { // 未选数据集:清空即可
|
if (currentDsId->isEmpty()) { // 未选数据集:清空即可
|
||||||
*prevDsId = *currentDsId;
|
*prevDsId = *currentDsId;
|
||||||
detailRenderWindowPtr->Render();
|
detailRenderWindowPtr->Render();
|
||||||
|
|
@ -822,6 +827,14 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
// ── 启动:建立一次空背景中央视图(真实 sections 数据由下一轮接入)。
|
// ── 启动:建立一次空背景中央视图(真实 sections 数据由下一轮接入)。
|
||||||
rebuildCentral();
|
rebuildCentral();
|
||||||
|
|
||||||
|
// VTK 背景随主题切换:直接重跑 rebuildCentral/rebuildDetail(走完整渲染路径、末尾必 Render,
|
||||||
|
// 比手动 SetBackground+Render 稳;兼顾 syncSystemTheme 异步切暗的时序)。
|
||||||
|
QObject::connect(eTheme, &ElaTheme::themeModeChanged, &window,
|
||||||
|
[rebuildCentral, rebuildDetail]() {
|
||||||
|
rebuildCentral();
|
||||||
|
rebuildDetail();
|
||||||
|
});
|
||||||
|
|
||||||
// 顶部应用区(静态视觉壳,对齐原型):上=菜单栏(视图/项目管理/业务工具/设备),
|
// 顶部应用区(静态视觉壳,对齐原型):上=菜单栏(视图/项目管理/业务工具/设备),
|
||||||
// 下=工具条(工作空间切换 + 项目 + 帮助/通知/设置 + 用户)。纵向堆叠后挂到主窗口顶部。
|
// 下=工具条(工作空间切换 + 项目 + 帮助/通知/设置 + 用户)。纵向堆叠后挂到主窗口顶部。
|
||||||
geopro::app::TopBar* topBar = nullptr;
|
geopro::app::TopBar* topBar = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "Glyphs.hpp"
|
#include "Glyphs.hpp"
|
||||||
|
#include "Theme.hpp"
|
||||||
#include "dto/NavDto.hpp"
|
#include "dto/NavDto.hpp"
|
||||||
|
|
||||||
namespace geopro::app {
|
namespace geopro::app {
|
||||||
|
|
@ -40,19 +41,19 @@ ObjectTreePanel::ObjectTreePanel(QWidget* parent) : QWidget(parent) {
|
||||||
{
|
{
|
||||||
const QString openArrow = writeChevronIcon(true, QColor("#8A93A3"));
|
const QString openArrow = writeChevronIcon(true, QColor("#8A93A3"));
|
||||||
const QString closedArrow = writeChevronIcon(false, QColor("#8A93A3"));
|
const QString closedArrow = writeChevronIcon(false, QColor("#8A93A3"));
|
||||||
tree_->setStyleSheet(
|
geopro::app::applyThemedStyleSheet(
|
||||||
QStringLiteral("QTreeView::branch { background: #FFFFFF; }"
|
tree_, QStringLiteral("QTreeView::branch { background: #FFFFFF; }"
|
||||||
"QTreeView::branch:has-children:!has-siblings:closed,"
|
"QTreeView::branch:has-children:!has-siblings:closed,"
|
||||||
"QTreeView::branch:closed:has-children:has-siblings { image: url(%1); }"
|
"QTreeView::branch:closed:has-children:has-siblings { image: url(%1); }"
|
||||||
"QTreeView::branch:open:has-children:!has-siblings,"
|
"QTreeView::branch:open:has-children:!has-siblings,"
|
||||||
"QTreeView::branch:open:has-children:has-siblings { image: url(%2); }")
|
"QTreeView::branch:open:has-children:has-siblings { image: url(%2); }")
|
||||||
.arg(closedArrow, openArrow));
|
.arg(closedArrow, openArrow));
|
||||||
}
|
}
|
||||||
lay->addWidget(tree_, 1);
|
lay->addWidget(tree_, 1);
|
||||||
|
|
||||||
hint_ = new QLabel(QStringLiteral("正在加载对象…"), this);
|
hint_ = new QLabel(QStringLiteral("正在加载对象…"), this);
|
||||||
hint_->setAlignment(Qt::AlignCenter);
|
hint_->setAlignment(Qt::AlignCenter);
|
||||||
hint_->setStyleSheet(QStringLiteral("color:#9AA6B6; padding:16px;"));
|
geopro::app::applyThemedStyleSheet(hint_, QStringLiteral("color:#9AA6B6; padding:16px;"));
|
||||||
hint_->setVisible(false);
|
hint_->setVisible(false);
|
||||||
lay->addWidget(hint_);
|
lay->addWidget(hint_);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue