153 lines
6.2 KiB
C++
153 lines
6.2 KiB
C++
#include "panels/DatasetListPanel.hpp"
|
||
|
||
#include <QColor>
|
||
#include <QListWidget>
|
||
#include <QListWidgetItem>
|
||
#include <QObject>
|
||
#include <QPainter>
|
||
#include <QPainterPath>
|
||
#include <QString>
|
||
#include <QStyledItemDelegate>
|
||
|
||
#include "Theme.hpp"
|
||
|
||
namespace geopro::app {
|
||
|
||
namespace {
|
||
QString humanSize(long long b) {
|
||
if (b < 1024) return QStringLiteral("%1 B").arg(b);
|
||
const double kb = b / 1024.0;
|
||
if (kb < 1024.0) return QStringLiteral("%1 KB").arg(kb, 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
|
||
|
||
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 QListWidgetItem(text, list);
|
||
item->setData(kDsIdRole, QString::fromStdString(d.id));
|
||
item->setData(kDsDdTypeRole, QString::fromStdString(d.ddCode));
|
||
}
|
||
}
|
||
|
||
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 QListWidgetItem(QStringLiteral("(暂无文件)"), list);
|
||
hint->setFlags(Qt::NoItemFlags);
|
||
hint->setTextAlignment(Qt::AlignCenter);
|
||
return;
|
||
}
|
||
for (const auto& d : rows) {
|
||
const QString fname =
|
||
d.fileName.empty() ? QString::fromStdString(d.dsName) : QString::fromStdString(d.fileName);
|
||
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 QListWidgetItem(text, list);
|
||
item->setData(kDsIdRole, QString::fromStdString(d.id));
|
||
item->setData(kDsFileUrlRole, QString::fromStdString(d.fileUrl));
|
||
}
|
||
}
|
||
|
||
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
|