新增根据帧数控制可见性
This commit is contained in:
264
client/gui/library/ResourceLibraryDock.cpp
Normal file
264
client/gui/library/ResourceLibraryDock.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
#include "library/ResourceLibraryDock.h"
|
||||
|
||||
#include "core/library/EntityJson.h"
|
||||
#include "core/library/ToolJson.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDrag>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QListWidget>
|
||||
#include <QMimeData>
|
||||
#include <QPainter>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include <QScrollBar>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace gui {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kMimeType = "application/x-hfut-resource+json";
|
||||
|
||||
QPixmap makePreviewPixmap(const core::library::LibraryResource& r) {
|
||||
const QSize sz = r.imageSize.isValid() ? r.imageSize : QSize(200, 200);
|
||||
QImage img(sz, QImage::Format_ARGB32_Premultiplied);
|
||||
img.fill(Qt::transparent);
|
||||
|
||||
QPainter p(&img);
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
QRectF rr(QPointF(0, 0), QSizeF(sz));
|
||||
rr = rr.adjusted(10, 10, -10, -10);
|
||||
p.setPen(QPen(QColor(0, 0, 0, 50), 2));
|
||||
p.setBrush(QBrush(r.accent));
|
||||
p.drawRoundedRect(rr, 18, 18);
|
||||
|
||||
p.setPen(QColor(255, 255, 255, 240));
|
||||
QFont f = p.font();
|
||||
f.setPointSize(std::max(9, f.pointSize()));
|
||||
f.setBold(true);
|
||||
p.setFont(f);
|
||||
p.drawText(rr.adjusted(12, 12, -12, -12), Qt::AlignLeft | Qt::AlignTop, r.displayName);
|
||||
|
||||
return QPixmap::fromImage(img);
|
||||
}
|
||||
|
||||
class ResourceListWidget final : public QListWidget {
|
||||
public:
|
||||
explicit ResourceListWidget(QWidget* parent = nullptr) : QListWidget(parent) {
|
||||
setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
setDragEnabled(true);
|
||||
setDragDropMode(QAbstractItemView::DragOnly);
|
||||
setDefaultDropAction(Qt::CopyAction);
|
||||
setUniformItemSizes(false);
|
||||
setSpacing(6);
|
||||
setViewMode(QListView::IconMode);
|
||||
setResizeMode(QListView::Adjust);
|
||||
// 注意:Static 会让拖拽“完全没反应”(不会启动 drag)。
|
||||
// 这里用 Free,仍然不会在列表内部重排(DragOnly),但允许拖出到画布。
|
||||
setMovement(QListView::Free);
|
||||
setIconSize(QSize(96, 96));
|
||||
setWordWrap(true);
|
||||
// “一行两个资源”:用网格尺寸 + 固定列宽近似实现
|
||||
setGridSize(QSize(180, 140));
|
||||
}
|
||||
|
||||
void setResources(const QVector<core::library::LibraryResource>& r) { m_resources = r; }
|
||||
|
||||
protected:
|
||||
void startDrag(Qt::DropActions supportedActions) override {
|
||||
Q_UNUSED(supportedActions);
|
||||
auto* item = currentItem();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
bool ok = false;
|
||||
const int idx = item->data(Qt::UserRole).toInt(&ok);
|
||||
if (!ok || idx < 0 || idx >= m_resources.size()) {
|
||||
return;
|
||||
}
|
||||
const auto& res = m_resources[idx];
|
||||
|
||||
QJsonObject payload;
|
||||
payload.insert(QStringLiteral("resourceId"), res.resourceId);
|
||||
payload.insert(QStringLiteral("displayName"), res.displayName);
|
||||
payload.insert(QStringLiteral("kind"), res.kind == core::library::LibraryResource::Kind::Tool ? QStringLiteral("tool")
|
||||
: QStringLiteral("entity"));
|
||||
if (res.kind == core::library::LibraryResource::Kind::Tool) {
|
||||
payload.insert(QStringLiteral("tool"), core::library::toolToJson(res.toolTemplate));
|
||||
} else {
|
||||
payload.insert(QStringLiteral("entity"), core::library::entityToJson(res.entityTemplate));
|
||||
}
|
||||
{
|
||||
QJsonArray a;
|
||||
a.append(res.imageSize.width());
|
||||
a.append(res.imageSize.height());
|
||||
payload.insert(QStringLiteral("imageSize"), a);
|
||||
}
|
||||
{
|
||||
QJsonArray a;
|
||||
a.append(res.accent.red());
|
||||
a.append(res.accent.green());
|
||||
a.append(res.accent.blue());
|
||||
a.append(res.accent.alpha());
|
||||
payload.insert(QStringLiteral("accent"), a);
|
||||
}
|
||||
{
|
||||
QJsonArray a;
|
||||
a.append(res.imageOffsetFromOrigin.x());
|
||||
a.append(res.imageOffsetFromOrigin.y());
|
||||
payload.insert(QStringLiteral("imageOffsetFromOrigin"), a);
|
||||
}
|
||||
|
||||
const QByteArray bytes = QJsonDocument(payload).toJson(QJsonDocument::Compact);
|
||||
auto* mime = new QMimeData();
|
||||
mime->setData(QString::fromUtf8(kMimeType), bytes);
|
||||
|
||||
auto* drag = new QDrag(this);
|
||||
drag->setMimeData(mime);
|
||||
drag->setPixmap(item->icon().pixmap(iconSize()));
|
||||
drag->setHotSpot(QPoint(iconSize().width() / 2, iconSize().height() / 2));
|
||||
drag->exec(Qt::CopyAction);
|
||||
}
|
||||
|
||||
private:
|
||||
QVector<core::library::LibraryResource> m_resources;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ResourceLibraryDock::ResourceLibraryDock(QWidget* parent)
|
||||
: QDockWidget(QStringLiteral("资源库"), parent) {
|
||||
setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
||||
setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable);
|
||||
|
||||
auto* shell = new QWidget(this);
|
||||
auto* layout = new QVBoxLayout(shell);
|
||||
layout->setContentsMargins(8, 8, 8, 8);
|
||||
layout->setSpacing(6);
|
||||
|
||||
m_list = new ResourceListWidget(shell);
|
||||
layout->addWidget(m_list, 1);
|
||||
if (m_list && m_list->verticalScrollBar()) {
|
||||
connect(m_list->verticalScrollBar(), &QScrollBar::valueChanged, this, [this](int value) {
|
||||
if (!m_list || !m_list->verticalScrollBar()) return;
|
||||
if (!m_onlineProvider || !m_onlineBound || m_onlineLoading || m_onlineExhausted) return;
|
||||
const int maxV = m_list->verticalScrollBar()->maximum();
|
||||
// 接近底部(阈值 80px):自动加载下一页
|
||||
if (value >= maxV - 80) {
|
||||
fetchOnlineNextPage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setWidget(shell);
|
||||
}
|
||||
|
||||
void ResourceLibraryDock::setProviders(core::library::ResourceLibraryProvider* localProvider,
|
||||
core::library::ResourceLibraryProvider* onlineProvider) {
|
||||
if (m_localProvider == localProvider && m_onlineProvider == onlineProvider) {
|
||||
return;
|
||||
}
|
||||
if (m_localProvider) disconnect(m_localProvider, nullptr, this, nullptr);
|
||||
if (m_onlineProvider) disconnect(m_onlineProvider, nullptr, this, nullptr);
|
||||
|
||||
m_localProvider = localProvider;
|
||||
m_onlineProvider = onlineProvider;
|
||||
m_onlineExhausted = false;
|
||||
|
||||
auto bind = [&](core::library::ResourceLibraryProvider* p,
|
||||
QVector<core::library::LibraryResource>& store,
|
||||
bool isOnline) {
|
||||
if (!p) {
|
||||
store.clear();
|
||||
return;
|
||||
}
|
||||
connect(p, &core::library::ResourceLibraryProvider::resourcesReady,
|
||||
this, [this, &store, isOnline](const QVector<core::library::LibraryResource>& res) {
|
||||
if (!isOnline) {
|
||||
store = res;
|
||||
} else {
|
||||
m_onlineLoading = false;
|
||||
if (res.isEmpty()) {
|
||||
m_onlineExhausted = true;
|
||||
return;
|
||||
}
|
||||
store += res;
|
||||
}
|
||||
rebuildCombinedList();
|
||||
});
|
||||
connect(p, &core::library::ResourceLibraryProvider::resourcesFailed,
|
||||
this, [this, isOnline](const QString& err) {
|
||||
Q_UNUSED(err);
|
||||
if (isOnline) {
|
||||
m_onlineLoading = false;
|
||||
// 在线错误默认不打扰 UI;下次滚动到底仍可重试(除非 provider 选择返回空 ready)。
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
bind(m_localProvider, m_localResources, false);
|
||||
bind(m_onlineProvider, m_onlineResources, true);
|
||||
|
||||
if (m_localProvider) {
|
||||
m_localProvider->fetchResourcesAsync();
|
||||
} else {
|
||||
m_localResources.clear();
|
||||
}
|
||||
rebuildCombinedList();
|
||||
ensureOnlineLoaded();
|
||||
}
|
||||
|
||||
void ResourceLibraryDock::ensureOnlineLoaded() {
|
||||
if (m_onlineBound) {
|
||||
return;
|
||||
}
|
||||
m_onlineBound = true;
|
||||
if (!m_onlineProvider) {
|
||||
return;
|
||||
}
|
||||
fetchOnlineNextPage();
|
||||
}
|
||||
|
||||
void ResourceLibraryDock::fetchOnlineNextPage() {
|
||||
if (!m_onlineProvider) {
|
||||
return;
|
||||
}
|
||||
if (m_onlineLoading) {
|
||||
return;
|
||||
}
|
||||
if (m_onlineExhausted) {
|
||||
return;
|
||||
}
|
||||
m_onlineLoading = true;
|
||||
// provider 当前仍是预留实现;这里保持“分页”调用语义(多次调用 fetchResourcesAsync)
|
||||
m_onlineProvider->fetchResourcesAsync();
|
||||
}
|
||||
|
||||
void ResourceLibraryDock::rebuildCombinedList() {
|
||||
if (!m_list) return;
|
||||
auto* list = static_cast<ResourceListWidget*>(m_list);
|
||||
list->clear();
|
||||
QVector<core::library::LibraryResource> combined;
|
||||
combined.reserve(m_localResources.size() + m_onlineResources.size());
|
||||
combined += m_localResources;
|
||||
combined += m_onlineResources;
|
||||
list->setResources(combined);
|
||||
for (int i = 0; i < combined.size(); ++i) {
|
||||
const auto& r = combined[i];
|
||||
auto* it = new QListWidgetItem();
|
||||
it->setFlags(it->flags() | Qt::ItemIsDragEnabled);
|
||||
it->setText(r.displayName);
|
||||
it->setIcon(QIcon(makePreviewPixmap(r)));
|
||||
it->setData(Qt::UserRole, i);
|
||||
it->setToolTip(r.resourceId);
|
||||
list->addItem(it);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
|
||||
38
client/gui/library/ResourceLibraryDock.h
Normal file
38
client/gui/library/ResourceLibraryDock.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/library/ResourceLibraryProvider.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
|
||||
class QListWidget;
|
||||
|
||||
namespace gui {
|
||||
|
||||
class ResourceLibraryDock final : public QDockWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ResourceLibraryDock(QWidget* parent = nullptr);
|
||||
~ResourceLibraryDock() override = default;
|
||||
|
||||
void setProviders(core::library::ResourceLibraryProvider* localProvider,
|
||||
core::library::ResourceLibraryProvider* onlineProvider);
|
||||
|
||||
private:
|
||||
void rebuildCombinedList();
|
||||
void ensureOnlineLoaded();
|
||||
void fetchOnlineNextPage();
|
||||
|
||||
private:
|
||||
core::library::ResourceLibraryProvider* m_localProvider = nullptr;
|
||||
core::library::ResourceLibraryProvider* m_onlineProvider = nullptr;
|
||||
QVector<core::library::LibraryResource> m_localResources;
|
||||
QVector<core::library::LibraryResource> m_onlineResources;
|
||||
|
||||
QListWidget* m_list = nullptr;
|
||||
bool m_onlineBound = false;
|
||||
bool m_onlineLoading = false;
|
||||
bool m_onlineExhausted = false;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
|
||||
Reference in New Issue
Block a user