feat(panels): 数据/文件列表卡片化(标题+元信息双行+选中竖条)(规范§6.2)

This commit is contained in:
gaozheng 2026-06-10 16:40:14 +08:00
parent 824e8bdf62
commit b26dcc1ca7
3 changed files with 102 additions and 0 deletions

View File

@ -544,9 +544,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
auto* datasetTabs = new QTabWidget(); auto* datasetTabs = new QTabWidget();
auto* datasetList = new QListWidget(); auto* datasetList = new QListWidget();
applyListSelection(datasetList); applyListSelection(datasetList);
geopro::app::applyDatasetCardDelegate(datasetList);
datasetTabs->addTab(datasetList, QStringLiteral("数据")); datasetTabs->addTab(datasetList, QStringLiteral("数据"));
auto* fileList = new QListWidget(); auto* fileList = new QListWidget();
applyListSelection(fileList); applyListSelection(fileList);
geopro::app::applyDatasetCardDelegate(fileList);
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(

View File

@ -3,7 +3,13 @@
#include <QColor> #include <QColor>
#include <QListWidget> #include <QListWidget>
#include <QListWidgetItem> #include <QListWidgetItem>
#include <QObject>
#include <QPainter>
#include <QPainterPath>
#include <QString> #include <QString>
#include <QStyledItemDelegate>
#include "Theme.hpp"
namespace geopro::app { namespace geopro::app {
@ -14,6 +20,88 @@ QString humanSize(long long b) {
if (kb < 1024.0) return QStringLiteral("%1 KB").arg(kb, 0, 'f', 1); if (kb < 1024.0) return QStringLiteral("%1 KB").arg(kb, 0, 'f', 1);
return QStringLiteral("%1 MB").arg(kb / 1024.0, 0, 'f', 1); return QStringLiteral("%1 MB").arg(kb / 1024.0, 0, 'f', 1);
} }
// 数据/文件列表卡片委托:标题+元信息双行、悬停/选中圆角高亮 + 选中左 2px 强调竖条规范§6.2)。
// 特殊行(加载更多 / 占位提示)退回为居中纯文本,不画卡片。
class DatasetCardDelegate : public QStyledItemDelegate {
public:
using QStyledItemDelegate::QStyledItemDelegate;
QSize sizeHint(const QStyleOptionViewItem&, const QModelIndex& idx) const override {
const bool special =
idx.data(kDsLoadMoreRole).toBool() || !(idx.flags() & Qt::ItemIsSelectable);
return QSize(0, special ? 34 : 52);
}
void paint(QPainter* p, const QStyleOptionViewItem& opt, const QModelIndex& idx) const override {
p->save();
p->setRenderHint(QPainter::Antialiasing, true);
const QString disp = idx.data(Qt::DisplayRole).toString();
// 「加载更多」居中强调色文本hover 时加底)。
if (idx.data(kDsLoadMoreRole).toBool()) {
if (opt.state & QStyle::State_MouseOver) {
QPainterPath bgp;
bgp.addRoundedRect(opt.rect.adjusted(4, 2, -4, -2), 6, 6);
p->fillPath(bgp, geopro::app::tokenColor("bg/hover"));
}
p->setPen(geopro::app::tokenColor("accent/primary"));
p->drawText(opt.rect, Qt::AlignCenter, disp);
p->restore();
return;
}
// 占位提示行(不可选):居中淡色文本。
if (!(idx.flags() & Qt::ItemIsSelectable)) {
p->setPen(geopro::app::tokenColor("text/disabled"));
p->drawText(opt.rect, Qt::AlignCenter, disp);
p->restore();
return;
}
// 卡片
const QRect r = opt.rect.adjusted(4, 2, -4, -2);
const bool selected = opt.state & QStyle::State_Selected;
const bool hover = opt.state & QStyle::State_MouseOver;
if (selected || hover) {
QPainterPath path;
path.addRoundedRect(r, 6, 6);
p->fillPath(path, geopro::app::tokenColor(selected ? "bg/selected" : "bg/hover"));
}
if (selected) { // 左 2px 强调竖条规范§6.2
p->fillRect(QRect(r.left(), r.top() + 4, 2, r.height() - 8),
geopro::app::tokenColor("accent/primary"));
}
QString title = disp, meta;
const int nl = disp.indexOf(QLatin1Char('\n'));
if (nl >= 0) {
title = disp.left(nl);
meta = disp.mid(nl + 1);
}
const QRect textR = r.adjusted(14, 6, -12, -6);
// 标题
QFont tf = opt.font;
tf.setPixelSize(geopro::app::scaledPx(13));
p->setFont(tf);
p->setPen(geopro::app::tokenColor("text/primary"));
const QRect titleR(textR.left(), textR.top(), textR.width(), textR.height() / 2);
p->drawText(titleR, Qt::AlignLeft | Qt::AlignVCenter,
p->fontMetrics().elidedText(title, Qt::ElideRight, titleR.width()));
// 元信息
if (!meta.isEmpty()) {
QFont mf = opt.font;
mf.setPixelSize(geopro::app::scaledPx(11));
p->setFont(mf);
p->setPen(geopro::app::tokenColor("text/tertiary"));
const QRect metaR(textR.left(), textR.center().y() + 1, textR.width(),
textR.height() / 2);
p->drawText(metaR, Qt::AlignLeft | Qt::AlignVCenter,
p->fontMetrics().elidedText(meta, Qt::ElideRight, metaR.width()));
}
p->restore();
}
};
} // namespace } // namespace
void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append) { void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append) {
@ -53,4 +141,13 @@ void populateFileList(QListWidget* list, const std::vector<geopro::data::DsRow>&
} }
} }
void applyDatasetCardDelegate(QListWidget* list) {
if (!list) return;
list->setItemDelegate(new DatasetCardDelegate(list));
list->setMouseTracking(true); // 让委托收到 hover 状态
list->setSpacing(0); // 卡间距由委托内边距控制
QObject::connect(&ThemeManager::instance(), &ThemeManager::changed, list,
[list]() { list->viewport()->update(); });
}
} // namespace geopro::app } // namespace geopro::app

View File

@ -18,4 +18,7 @@ void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsRo
// 文件页签:每条 = 文件名 +可读大小UserRole 存 dsId、+2 存文件 url。空时显示占位。 // 文件页签:每条 = 文件名 +可读大小UserRole 存 dsId、+2 存文件 url。空时显示占位。
void populateFileList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append); void populateFileList(QListWidget* list, const std::vector<geopro::data::DsRow>& rows, bool append);
// 给数据/文件列表套用卡片委托(标题+元信息双行、悬停/选中圆角高亮+左强调竖条规范§6.2)。
void applyDatasetCardDelegate(QListWidget* list);
} // namespace geopro::app } // namespace geopro::app