fix(list): 数据/文件/异常列表退回标准 QListWidget + 写死强选中色

根因: ElaListView 的选中底走 BasicSelectedAlpha, setThemeColor 改它对 ElaListView 不生效(自绘控件坑),
选中色无法变强。与对象树同理, 退回 Qt 原生 QListWidget:
- 3 列表 ElaListView+QStandardItemModel → QListWidget+QListWidgetItem(populate/加载更多/点击/勾选 全回退)
- applyListSelection: 本地 QSS 写死强调蓝选中(明 #C2D9F2 / 暗 #33527A + 适配文字, :!active 防失焦淡),
  与对象树选中色完全一致, 100% 可控、明暗都清晰
- 行为(加载更多/数据集点击/异常勾选显隐)保持
This commit is contained in:
gaozheng 2026-06-10 12:39:23 +08:00
parent 66cf432a98
commit 52bdf054a6
5 changed files with 88 additions and 88 deletions

View File

@ -36,9 +36,8 @@
#include <QHBoxLayout>
#include <QGraphicsOpacityEffect>
#include <QLabel>
#include <QModelIndex>
#include <QStandardItem>
#include <QStandardItemModel>
#include <QListWidget>
#include <QListWidgetItem>
#include <QToolButton>
#include <QKeySequence>
#include <QProcess>
@ -68,7 +67,6 @@
#include <ElaApplication.h>
#include <ElaCheckBox.h>
#include <ElaDef.h>
#include <ElaListView.h>
#include <ElaTheme.h>
#include <ElaToolButton.h>
#include <ElaWindow.h>
@ -544,15 +542,30 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
{{geopro::app::Glyph::Plus, QStringLiteral("新建对象")}}));
auto* leftArea = dockManager->addDockWidget(ads::LeftDockWidgetArea, leftDock);
// 列表选中色:写死的强调蓝(明 #C2D9F2 / 暗 #33527A+ 适配文字,:!active 防失焦变淡;
// 与对象树选中色一致。本地 QSS 覆盖全局弱选中色,随主题重设。
auto applyListSelection = [](QListWidget* lw) {
auto styleIt = [lw]() {
const bool dark = geopro::app::isDarkTheme();
const QString selBg = dark ? QStringLiteral("#33527A") : QStringLiteral("#C2D9F2");
const QString selFg = dark ? QStringLiteral("#E8F1FB") : QStringLiteral("#14385F");
lw->setStyleSheet(QStringLiteral("QListWidget::item:selected{ background:%1; color:%2; }"
"QListWidget::item:selected:!active{ background:%1;"
" color:%2; }")
.arg(selBg, selFg));
};
styleIt();
QObject::connect(eTheme, &ElaTheme::themeModeChanged, lw,
[styleIt](ElaThemeType::ThemeMode) { styleIt(); });
};
// 左下 dock数据真实显示栏(选中测线后列其采集批次=数据集;tab 数据/文件)。
auto* datasetTabs = new QTabWidget();
auto* datasetList = new ElaListView(); // Fluent 列表(自绘 hover/选中, 随主题)
auto* datasetModel = new QStandardItemModel(datasetList);
datasetList->setModel(datasetModel);
auto* datasetList = new QListWidget();
applyListSelection(datasetList);
datasetTabs->addTab(datasetList, QStringLiteral("数据"));
auto* fileList = new ElaListView();
auto* fileModel = new QStandardItemModel(fileList);
fileList->setModel(fileModel);
auto* fileList = new QListWidget();
applyListSelection(fileList);
datasetTabs->addTab(fileList, QStringLiteral("文件"));
auto* datasetDock = new ads::CDockWidget(QStringLiteral("数据真实显示栏"));
auto* datasetBox = wrapWithHeader(
@ -565,9 +578,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
dockManager->addDockWidget(ads::BottomDockWidgetArea, datasetDock, leftArea);
// 右上 dock异常列表 / 对象属性 合并为带 Tab 表头的面板(对齐原型上半)。
auto* anomalyList = new ElaListView();
auto* anomalyModel = new QStandardItemModel(anomalyList);
anomalyList->setModel(anomalyModel);
auto* anomalyList = new QListWidget();
applyListSelection(anomalyList);
auto* objAttrLabel = new QLabel(QStringLiteral("(选中对象后显示其属性)"));
objAttrLabel->setWordWrap(true);
objAttrLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
@ -727,7 +739,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
};
// 加载某数据集到「数据详情 + 异常列表 + 属性」(数据列表单击与启动默认共用)。
auto loadDataset = [&repo, propLabel, currentDsId, rebuildDetail, anomalyModel, hiddenAnoms,
auto loadDataset = [&repo, propLabel, currentDsId, rebuildDetail, anomalyList, hiddenAnoms,
anomalyBadge](const QString& dsId, const QString& name) {
if (dsId.isEmpty()) return;
*currentDsId = dsId;
@ -736,8 +748,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
const auto anomalies = repo.loadAnomalies(dsId.toStdString());
hiddenAnoms->clear();
{
const QSignalBlocker block(anomalyModel); // 重填触发 itemChanged先屏蔽
geopro::app::populateAnomalyList(anomalyModel, anomalies);
const QSignalBlocker block(anomalyList); // 重填触发 itemChanged先屏蔽
geopro::app::populateAnomalyList(anomalyList, anomalies);
}
// 异常列表 Tab 数量徽标。
if (anomalyBadge) {
@ -763,13 +775,14 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
// ── 单击左下数据列表的采集批次(DS) → 占位(真实剖面/反演渲染下一阶段接 dd 接口)──
// 接 dd 那轮:把本处占位改为 loadDataset(id, name) 即接通详情渲染,并自动激活 overdrive-A 揭示动画。
QObject::connect(datasetList, &QAbstractItemView::clicked, datasetList,
[propLabel, detailRendererPtr, detailRenderWindowPtr, &nav](const QModelIndex& idx) {
if (idx.data(geopro::app::kDsLoadMoreRole).toBool()) {
QObject::connect(datasetList, &QListWidget::itemClicked, datasetList,
[propLabel, detailRendererPtr, detailRenderWindowPtr, &nav](QListWidgetItem* item) {
if (item->data(geopro::app::kDsLoadMoreRole).toBool()) {
nav.loadMoreData();
return;
}
const QString name = idx.data(Qt::DisplayRole).toString().section('\n', 0, 0);
const QString name =
item->data(Qt::DisplayRole).toString().section('\n', 0, 0);
detailRendererPtr->RemoveAllViewProps();
detailRenderWindowPtr->Render();
propLabel->setText(QStringLiteral(
@ -777,8 +790,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
});
// ── 异常列表勾选(显隐) → 更新隐藏集 → 重建数据详情 ──
QObject::connect(anomalyModel, &QStandardItemModel::itemChanged, anomalyList,
[hiddenAnoms, rebuildDetail](QStandardItem* item) {
QObject::connect(anomalyList, &QListWidget::itemChanged, anomalyList,
[hiddenAnoms, rebuildDetail](QListWidgetItem* item) {
const int idx = item->data(geopro::app::kAnomalyIndexRole).toInt();
if (item->checkState() == Qt::Checked)
hiddenAnoms->erase(idx);
@ -891,20 +904,18 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
// ── 控制器 ↔ UI 信号接线(导航壳)──────────────────────────────────────
// "加载更多"行:列表末尾若已加载数 < 总数,放一行可点击的"加载更多(已/共)"。
auto removeLoadMore = [](QStandardItemModel* mdl) {
const int n = mdl->rowCount();
if (n > 0 && mdl->item(n - 1)->data(geopro::app::kDsLoadMoreRole).toBool())
mdl->removeRow(n - 1);
auto removeLoadMore = [](QListWidget* lw) {
if (lw->count() > 0 &&
lw->item(lw->count() - 1)->data(geopro::app::kDsLoadMoreRole).toBool())
delete lw->takeItem(lw->count() - 1);
};
auto addLoadMore = [](QStandardItemModel* mdl, int total) {
const int loaded = mdl->rowCount();
auto addLoadMore = [](QListWidget* lw, int total) {
const int loaded = lw->count();
if (loaded < total) {
auto* it = new QStandardItem(QStringLiteral("加载更多(%1/%2").arg(loaded).arg(total));
it->setData(true, geopro::app::kDsLoadMoreRole);
it->setTextAlignment(Qt::AlignCenter);
it->setForeground(QColor("#2D6CB5"));
it->setEditable(false);
mdl->appendRow(it);
auto* m = new QListWidgetItem(QStringLiteral("加载更多(%1/%2").arg(loaded).arg(total), lw);
m->setData(geopro::app::kDsLoadMoreRole, true);
m->setTextAlignment(Qt::AlignCenter);
m->setForeground(QColor("#2D6CB5"));
}
return loaded;
};
@ -943,42 +954,42 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
topBar->setProjects(list, cur, total > static_cast<int>(list.size()));
});
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::structureLoaded, objectTree,
[objectTree, datasetModel, fileModel, datasetTitle, datasetTabs](
[objectTree, datasetList, fileList, datasetTitle, datasetTabs](
const QString& projectName,
const std::vector<geopro::data::StructNode>& nodes) {
objectTree->setStructure(projectName, nodes);
datasetModel->clear();
fileModel->clear();
datasetList->clear();
fileList->clear();
if (datasetTitle) datasetTitle->setText(QStringLiteral("数据集显示栏"));
datasetTabs->setTabText(0, QStringLiteral("数据"));
datasetTabs->setTabText(1, QStringLiteral("文件"));
});
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::datasetsLoaded, datasetList,
[removeLoadMore, addLoadMore, datasetModel, datasetTitle, datasetTabs](
[removeLoadMore, addLoadMore, datasetList, datasetTitle, datasetTabs](
const QString&, const std::vector<geopro::data::DsRow>& rows, int total,
bool append) {
removeLoadMore(datasetModel);
geopro::app::populateDatasetList(datasetModel, rows, append);
const int loaded = addLoadMore(datasetModel, total);
removeLoadMore(datasetList);
geopro::app::populateDatasetList(datasetList, rows, append);
const int loaded = addLoadMore(datasetList, total);
if (datasetTitle) datasetTitle->setText(QStringLiteral("数据集显示栏"));
datasetTabs->setTabText(
0, total > 0 ? QStringLiteral("数据 (%1/%2)").arg(loaded).arg(total)
: QStringLiteral("数据"));
});
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::filesLoaded, fileList,
[removeLoadMore, addLoadMore, fileModel, datasetTabs](
[removeLoadMore, addLoadMore, fileList, datasetTabs](
const QString&, const std::vector<geopro::data::DsRow>& rows, int total,
bool append) {
removeLoadMore(fileModel);
geopro::app::populateFileList(fileModel, rows, append);
const int loaded = addLoadMore(fileModel, total);
removeLoadMore(fileList);
geopro::app::populateFileList(fileList, rows, append);
const int loaded = addLoadMore(fileList, total);
datasetTabs->setTabText(
1, total > 0 ? QStringLiteral("文件 (%1/%2)").arg(loaded).arg(total)
: QStringLiteral("文件"));
});
QObject::connect(fileList, &QAbstractItemView::clicked, fileList,
[&nav](const QModelIndex& idx) {
if (idx.data(geopro::app::kDsLoadMoreRole).toBool()) nav.loadMoreFiles();
QObject::connect(fileList, &QListWidget::itemClicked, fileList,
[&nav](QListWidgetItem* item) {
if (item->data(geopro::app::kDsLoadMoreRole).toBool()) nav.loadMoreFiles();
});
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::loadFailed, objectTree,
[objectTree, &window](const QString& stage, const QString& msg) {

View File

@ -5,9 +5,9 @@
#include <QColor>
#include <QIcon>
#include <QListWidget>
#include <QListWidgetItem>
#include <QPixmap>
#include <QStandardItem>
#include <QStandardItemModel>
#include <QString>
#include "model/ColorScale.hpp"
@ -54,10 +54,10 @@ QPixmap swatch(const std::string& colorStr)
} // namespace
void populateAnomalyList(QStandardItemModel* model, const std::vector<geopro::core::Anomaly>& anomalies)
void populateAnomalyList(QListWidget* list, const std::vector<geopro::core::Anomaly>& anomalies)
{
if (!model) return;
model->clear();
if (!list) return;
list->clear();
for (std::size_t i = 0; i < anomalies.size(); ++i) {
const auto& a = anomalies[i];
const QString name = QString::fromStdString(a.name.empty() ? "异常" : a.name);
@ -66,12 +66,10 @@ void populateAnomalyList(QStandardItemModel* model, const std::vector<geopro::co
if (!type.isEmpty()) text += QStringLiteral("%1").arg(type);
text += QStringLiteral("\n%1").arg(summarize(a));
auto* item = new QStandardItem(QIcon(swatch(a.lineColor)), text);
item->setEditable(false);
item->setData(static_cast<int>(i), kAnomalyIndexRole);
item->setCheckable(true);
auto* item = new QListWidgetItem(QIcon(swatch(a.lineColor)), text, list);
item->setData(kAnomalyIndexRole, static_cast<int>(i));
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Checked); // 默认显示
model->appendRow(item);
}
}

View File

@ -3,7 +3,7 @@
#include "model/Anomaly.hpp"
class QStandardItemModel;
class QListWidget;
namespace geopro::app {
@ -14,6 +14,6 @@ constexpr int kAnomalyIndexRole = 0x0100; // Qt::UserRole
// 派生「位置 Xm · 深 Ym · 尺寸 Zm」(由 location.coordinate 质心/包络算)。
// 条目可勾选:勾=显示(默认全勾);勾选状态变化由调用方连接驱动该异常 actor 显隐。
// 清空旧条目后重填。
void populateAnomalyList(QStandardItemModel* model, const std::vector<geopro::core::Anomaly>& anomalies);
void populateAnomalyList(QListWidget* list, const std::vector<geopro::core::Anomaly>& anomalies);
} // namespace geopro::app

View File

@ -1,8 +1,8 @@
#include "panels/DatasetListPanel.hpp"
#include <QColor>
#include <QStandardItem>
#include <QStandardItemModel>
#include <QListWidget>
#include <QListWidgetItem>
#include <QString>
namespace geopro::app {
@ -16,34 +16,29 @@ QString humanSize(long long b) {
}
} // namespace
void populateDatasetList(QStandardItemModel* model, const std::vector<geopro::data::DsRow>& rows,
bool append) {
if (!model) return;
if (!append) model->clear();
void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append) {
if (!list) return;
if (!append) list->clear();
for (const auto& d : rows) {
QString text = QString::fromStdString(d.dsName);
QString sub = QString::fromStdString(d.createTime); // 名称下先创建时间
if (!d.typeName.empty())
sub += QStringLiteral(" · %1").arg(QString::fromStdString(d.typeName)); // 再跟类型
if (!sub.isEmpty()) text += QStringLiteral("\n%1").arg(sub);
auto* item = new QStandardItem(text);
item->setEditable(false);
item->setData(QString::fromStdString(d.id), kDsIdRole);
item->setData(QString::fromStdString(d.ddCode), kDsDdTypeRole);
model->appendRow(item);
auto* item = new QListWidgetItem(text, list);
item->setData(kDsIdRole, QString::fromStdString(d.id));
item->setData(kDsDdTypeRole, QString::fromStdString(d.ddCode));
}
}
void populateFileList(QStandardItemModel* model, const std::vector<geopro::data::DsRow>& rows,
bool append) {
if (!model) return;
if (!append) model->clear();
void populateFileList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append) {
if (!list) return;
if (!append) list->clear();
if (!append && rows.empty()) {
auto* hint = new QStandardItem(QStringLiteral("(暂无文件)"));
auto* hint = new QListWidgetItem(QStringLiteral("(暂无文件)"), list);
hint->setFlags(Qt::NoItemFlags);
hint->setForeground(QColor("#9AA6B6"));
hint->setTextAlignment(Qt::AlignCenter);
model->appendRow(hint);
return;
}
for (const auto& d : rows) {
@ -52,11 +47,9 @@ void populateFileList(QStandardItemModel* model, const std::vector<geopro::data:
QString sub = QString::fromStdString(d.createTime); // 名称下先创建时间
sub += QStringLiteral(" · %1").arg(humanSize(d.fileSize)); // 再跟大小
const QString text = fname + QStringLiteral("\n%1").arg(sub);
auto* item = new QStandardItem(text);
item->setEditable(false);
item->setData(QString::fromStdString(d.id), kDsIdRole);
item->setData(QString::fromStdString(d.fileUrl), kDsFileUrlRole);
model->appendRow(item);
auto* item = new QListWidgetItem(text, list);
item->setData(kDsIdRole, QString::fromStdString(d.id));
item->setData(kDsFileUrlRole, QString::fromStdString(d.fileUrl));
}
}

View File

@ -3,7 +3,7 @@
#include "repo/RepoTypes.hpp"
class QStandardItemModel;
class QListWidget;
namespace geopro::app {
@ -13,11 +13,9 @@ constexpr int kDsDdTypeRole = 0x0101; // Qt::UserRole + 1
constexpr int kDsFileUrlRole = 0x0102; // Qt::UserRole + 2文件下载 url备用
constexpr int kDsLoadMoreRole = 0x0103; // 标记"加载更多"行
// 数据页签:每条 = dsName +类型名UserRole 存 dsId、+1 存 ddCode。填入标准 model。
void populateDatasetList(QStandardItemModel* model, const std::vector<geopro::data::DsRow>& rows,
bool append);
// 数据页签:每条 = dsName +类型名UserRole 存 dsId、+1 存 ddCode。
void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append);
// 文件页签:每条 = 文件名 +可读大小UserRole 存 dsId、+2 存文件 url。空时显示占位。
void populateFileList(QStandardItemModel* model, const std::vector<geopro::data::DsRow>& rows,
bool append);
void populateFileList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append);
} // namespace geopro::app