2022-10-18 09:35:04 +00:00
|
|
|
#include "material_list_widget.h"
|
|
|
|
#include "document.h"
|
|
|
|
#include <QApplication>
|
|
|
|
#include <QClipboard>
|
2018-10-09 02:19:12 +00:00
|
|
|
#include <QGuiApplication>
|
|
|
|
#include <QMenu>
|
|
|
|
#include <QXmlStreamWriter>
|
2021-11-18 14:58:01 +00:00
|
|
|
#include <dust3d/base/snapshot_xml.h>
|
2018-10-09 02:19:12 +00:00
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
MaterialListWidget::MaterialListWidget(const Document* document, QWidget* parent)
|
|
|
|
: QTreeWidget(parent)
|
|
|
|
, m_document(document)
|
2018-10-09 02:19:12 +00:00
|
|
|
{
|
|
|
|
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
|
|
|
setFocusPolicy(Qt::NoFocus);
|
|
|
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
|
|
|
|
setAutoScroll(false);
|
|
|
|
|
|
|
|
setHeaderHidden(true);
|
|
|
|
|
|
|
|
QPalette palette = this->palette();
|
|
|
|
palette.setColor(QPalette::Window, Qt::transparent);
|
|
|
|
palette.setColor(QPalette::Base, Qt::transparent);
|
|
|
|
setPalette(palette);
|
|
|
|
|
|
|
|
setStyleSheet("QTreeView {qproperty-indentation: 0;}");
|
|
|
|
|
|
|
|
setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
2018-10-25 00:19:38 +00:00
|
|
|
connect(document, &Document::materialListChanged, this, &MaterialListWidget::reload);
|
|
|
|
connect(document, &Document::cleanup, this, &MaterialListWidget::removeAllContent);
|
2018-10-09 02:19:12 +00:00
|
|
|
|
2018-10-25 00:19:38 +00:00
|
|
|
connect(this, &MaterialListWidget::removeMaterial, document, &Document::removeMaterial);
|
2018-10-09 02:19:12 +00:00
|
|
|
|
|
|
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
connect(this, &QTreeWidget::customContextMenuRequested, this, &MaterialListWidget::showContextMenu);
|
|
|
|
|
|
|
|
reload();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MaterialListWidget::enableMultipleSelection(bool enabled)
|
|
|
|
{
|
|
|
|
m_multipleSelectionEnabled = enabled;
|
|
|
|
}
|
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
void MaterialListWidget::materialRemoved(dust3d::Uuid materialId)
|
2018-10-09 02:19:12 +00:00
|
|
|
{
|
|
|
|
if (m_currentSelectedMaterialId == materialId)
|
2021-11-18 14:58:01 +00:00
|
|
|
m_currentSelectedMaterialId = dust3d::Uuid();
|
2018-10-09 02:19:12 +00:00
|
|
|
m_selectedMaterialIds.erase(materialId);
|
|
|
|
m_itemMap.erase(materialId);
|
|
|
|
}
|
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
void MaterialListWidget::updateMaterialSelectState(dust3d::Uuid materialId, bool selected)
|
2018-10-09 02:19:12 +00:00
|
|
|
{
|
|
|
|
auto findItemResult = m_itemMap.find(materialId);
|
|
|
|
if (findItemResult == m_itemMap.end()) {
|
|
|
|
qDebug() << "Find material item failed:" << materialId;
|
|
|
|
return;
|
|
|
|
}
|
2022-10-18 09:35:04 +00:00
|
|
|
MaterialWidget* materialWidget = (MaterialWidget*)itemWidget(findItemResult->second.first, findItemResult->second.second);
|
2018-10-09 02:19:12 +00:00
|
|
|
materialWidget->updateCheckedState(selected);
|
|
|
|
}
|
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
void MaterialListWidget::selectMaterial(dust3d::Uuid materialId, bool multiple)
|
2018-10-09 02:19:12 +00:00
|
|
|
{
|
|
|
|
if (multiple) {
|
|
|
|
if (!m_currentSelectedMaterialId.isNull()) {
|
|
|
|
m_selectedMaterialIds.insert(m_currentSelectedMaterialId);
|
2021-11-18 14:58:01 +00:00
|
|
|
m_currentSelectedMaterialId = dust3d::Uuid();
|
2018-10-09 02:19:12 +00:00
|
|
|
}
|
|
|
|
if (m_selectedMaterialIds.find(materialId) != m_selectedMaterialIds.end()) {
|
|
|
|
updateMaterialSelectState(materialId, false);
|
|
|
|
m_selectedMaterialIds.erase(materialId);
|
|
|
|
} else {
|
|
|
|
if (m_multipleSelectionEnabled || m_selectedMaterialIds.empty()) {
|
|
|
|
updateMaterialSelectState(materialId, true);
|
|
|
|
m_selectedMaterialIds.insert(materialId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_selectedMaterialIds.size() > 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m_selectedMaterialIds.size() == 1)
|
|
|
|
materialId = *m_selectedMaterialIds.begin();
|
|
|
|
else {
|
2021-11-18 14:58:01 +00:00
|
|
|
materialId = dust3d::Uuid();
|
2018-10-09 02:19:12 +00:00
|
|
|
emit currentSelectedMaterialChanged(materialId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!m_selectedMaterialIds.empty()) {
|
2022-10-18 09:35:04 +00:00
|
|
|
for (const auto& id : m_selectedMaterialIds) {
|
2018-10-09 02:19:12 +00:00
|
|
|
updateMaterialSelectState(id, false);
|
|
|
|
}
|
|
|
|
m_selectedMaterialIds.clear();
|
|
|
|
}
|
|
|
|
if (m_currentSelectedMaterialId != materialId) {
|
|
|
|
if (!m_currentSelectedMaterialId.isNull()) {
|
|
|
|
updateMaterialSelectState(m_currentSelectedMaterialId, false);
|
|
|
|
}
|
|
|
|
m_currentSelectedMaterialId = materialId;
|
|
|
|
if (!m_currentSelectedMaterialId.isNull()) {
|
|
|
|
updateMaterialSelectState(m_currentSelectedMaterialId, true);
|
|
|
|
}
|
|
|
|
emit currentSelectedMaterialChanged(m_currentSelectedMaterialId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
void MaterialListWidget::mousePressEvent(QMouseEvent* event)
|
2018-10-09 02:19:12 +00:00
|
|
|
{
|
|
|
|
QModelIndex itemIndex = indexAt(event->pos());
|
|
|
|
QTreeView::mousePressEvent(event);
|
|
|
|
if (event->button() == Qt::LeftButton) {
|
|
|
|
bool multiple = QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier);
|
|
|
|
if (itemIndex.isValid()) {
|
2022-10-18 09:35:04 +00:00
|
|
|
QTreeWidgetItem* item = itemFromIndex(itemIndex);
|
2021-11-18 14:58:01 +00:00
|
|
|
auto materialId = dust3d::Uuid(item->data(itemIndex.column(), Qt::UserRole).toString().toUtf8().constData());
|
2018-10-09 02:19:12 +00:00
|
|
|
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) {
|
|
|
|
bool startAdd = false;
|
|
|
|
bool stopAdd = false;
|
2021-11-18 14:58:01 +00:00
|
|
|
std::vector<dust3d::Uuid> waitQueue;
|
2022-10-18 09:35:04 +00:00
|
|
|
for (const auto& childId : m_document->materialIdList) {
|
2018-10-09 02:19:12 +00:00
|
|
|
if (m_shiftStartMaterialId == childId || materialId == childId) {
|
|
|
|
if (startAdd) {
|
|
|
|
stopAdd = true;
|
|
|
|
} else {
|
|
|
|
startAdd = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (startAdd)
|
|
|
|
waitQueue.push_back(childId);
|
|
|
|
if (stopAdd)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (stopAdd && !waitQueue.empty()) {
|
|
|
|
if (!m_selectedMaterialIds.empty()) {
|
2022-10-18 09:35:04 +00:00
|
|
|
for (const auto& id : m_selectedMaterialIds) {
|
2018-10-09 02:19:12 +00:00
|
|
|
updateMaterialSelectState(id, false);
|
|
|
|
}
|
|
|
|
m_selectedMaterialIds.clear();
|
|
|
|
}
|
|
|
|
if (!m_currentSelectedMaterialId.isNull()) {
|
2021-11-18 14:58:01 +00:00
|
|
|
m_currentSelectedMaterialId = dust3d::Uuid();
|
2018-10-09 02:19:12 +00:00
|
|
|
}
|
2022-10-18 09:35:04 +00:00
|
|
|
for (const auto& waitId : waitQueue) {
|
2018-10-09 02:19:12 +00:00
|
|
|
selectMaterial(waitId, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
m_shiftStartMaterialId = materialId;
|
|
|
|
}
|
|
|
|
selectMaterial(materialId, multiple);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!multiple)
|
2021-11-18 14:58:01 +00:00
|
|
|
selectMaterial(dust3d::Uuid());
|
2018-10-09 02:19:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
bool MaterialListWidget::isMaterialSelected(dust3d::Uuid materialId)
|
2018-10-09 02:19:12 +00:00
|
|
|
{
|
2022-10-18 09:35:04 +00:00
|
|
|
return (m_currentSelectedMaterialId == materialId || m_selectedMaterialIds.find(materialId) != m_selectedMaterialIds.end());
|
2018-10-09 02:19:12 +00:00
|
|
|
}
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
void MaterialListWidget::showContextMenu(const QPoint& pos)
|
2018-10-09 02:19:12 +00:00
|
|
|
{
|
|
|
|
if (!m_hasContextMenu)
|
|
|
|
return;
|
|
|
|
|
|
|
|
QMenu contextMenu(this);
|
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
std::set<dust3d::Uuid> unorderedMaterialIds = m_selectedMaterialIds;
|
2018-10-09 02:19:12 +00:00
|
|
|
if (!m_currentSelectedMaterialId.isNull())
|
|
|
|
unorderedMaterialIds.insert(m_currentSelectedMaterialId);
|
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
std::vector<dust3d::Uuid> materialIds;
|
2022-10-18 09:35:04 +00:00
|
|
|
for (const auto& cand : m_document->materialIdList) {
|
2018-10-09 02:19:12 +00:00
|
|
|
if (unorderedMaterialIds.find(cand) != unorderedMaterialIds.end())
|
|
|
|
materialIds.push_back(cand);
|
|
|
|
}
|
|
|
|
|
|
|
|
QAction modifyAction(tr("Modify"), this);
|
|
|
|
if (materialIds.size() == 1) {
|
|
|
|
connect(&modifyAction, &QAction::triggered, this, [=]() {
|
|
|
|
emit modifyMaterial(*materialIds.begin());
|
|
|
|
});
|
|
|
|
contextMenu.addAction(&modifyAction);
|
|
|
|
}
|
|
|
|
|
|
|
|
QAction copyAction(tr("Copy"), this);
|
|
|
|
if (!materialIds.empty()) {
|
|
|
|
connect(©Action, &QAction::triggered, this, &MaterialListWidget::copy);
|
|
|
|
contextMenu.addAction(©Action);
|
|
|
|
}
|
|
|
|
|
|
|
|
QAction pasteAction(tr("Paste"), this);
|
|
|
|
if (m_document->hasPastableMaterialsInClipboard()) {
|
2018-10-25 00:19:38 +00:00
|
|
|
connect(&pasteAction, &QAction::triggered, m_document, &Document::paste);
|
2018-10-09 02:19:12 +00:00
|
|
|
contextMenu.addAction(&pasteAction);
|
|
|
|
}
|
|
|
|
|
|
|
|
QAction deleteAction(tr("Delete"), this);
|
|
|
|
if (!materialIds.empty()) {
|
|
|
|
connect(&deleteAction, &QAction::triggered, [=]() {
|
2022-10-18 09:35:04 +00:00
|
|
|
for (const auto& materialId : materialIds)
|
2018-10-09 02:19:12 +00:00
|
|
|
emit removeMaterial(materialId);
|
|
|
|
});
|
|
|
|
contextMenu.addAction(&deleteAction);
|
|
|
|
}
|
|
|
|
|
|
|
|
contextMenu.exec(mapToGlobal(pos));
|
|
|
|
}
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
void MaterialListWidget::resizeEvent(QResizeEvent* event)
|
2018-10-09 02:19:12 +00:00
|
|
|
{
|
|
|
|
QTreeWidget::resizeEvent(event);
|
|
|
|
if (calculateColumnCount() != columnCount())
|
|
|
|
reload();
|
|
|
|
}
|
|
|
|
|
|
|
|
int MaterialListWidget::calculateColumnCount()
|
|
|
|
{
|
|
|
|
if (nullptr == parentWidget())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int columns = parentWidget()->width() / Theme::materialPreviewImageSize;
|
|
|
|
if (0 == columns)
|
|
|
|
columns = 1;
|
|
|
|
return columns;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MaterialListWidget::reload()
|
|
|
|
{
|
|
|
|
removeAllContent();
|
|
|
|
|
|
|
|
int columns = calculateColumnCount();
|
|
|
|
if (0 == columns)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int columnWidth = parentWidget()->width() / columns;
|
|
|
|
|
|
|
|
//qDebug() << "parentWidth:" << parentWidget()->width() << "columnWidth:" << columnWidth << "columns:" << columns;
|
|
|
|
|
|
|
|
setColumnCount(columns);
|
|
|
|
for (int i = 0; i < columns; i++)
|
|
|
|
setColumnWidth(i, columnWidth);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
std::vector<dust3d::Uuid> orderedMaterialIdList = m_document->materialIdList;
|
2022-10-18 09:35:04 +00:00
|
|
|
std::sort(orderedMaterialIdList.begin(), orderedMaterialIdList.end(), [&](const dust3d::Uuid& firstMaterialId, const dust3d::Uuid& secondMaterialId) {
|
|
|
|
const auto* firstMaterial = m_document->findMaterial(firstMaterialId);
|
|
|
|
const auto* secondMaterial = m_document->findMaterial(secondMaterialId);
|
2019-06-25 21:54:22 +00:00
|
|
|
if (nullptr == firstMaterial || nullptr == secondMaterial)
|
|
|
|
return false;
|
|
|
|
return QString::compare(firstMaterial->name, secondMaterial->name, Qt::CaseInsensitive) < 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
decltype(orderedMaterialIdList.size()) materialIndex = 0;
|
|
|
|
while (materialIndex < orderedMaterialIdList.size()) {
|
2022-10-18 09:35:04 +00:00
|
|
|
QTreeWidgetItem* item = new QTreeWidgetItem(this);
|
2018-10-09 02:19:12 +00:00
|
|
|
item->setFlags((item->flags() | Qt::ItemIsEnabled) & ~(Qt::ItemIsSelectable) & ~(Qt::ItemIsEditable));
|
2019-06-25 21:54:22 +00:00
|
|
|
for (int col = 0; col < columns && materialIndex < orderedMaterialIdList.size(); col++, materialIndex++) {
|
2022-10-18 09:35:04 +00:00
|
|
|
const auto& materialId = orderedMaterialIdList[materialIndex];
|
2018-10-09 02:19:12 +00:00
|
|
|
item->setSizeHint(col, QSize(columnWidth, MaterialWidget::preferredHeight() + 2));
|
2021-11-18 14:58:01 +00:00
|
|
|
item->setData(col, Qt::UserRole, QString(materialId.toString().c_str()));
|
2022-10-18 09:35:04 +00:00
|
|
|
MaterialWidget* widget = new MaterialWidget(m_document, materialId);
|
2018-10-09 02:19:12 +00:00
|
|
|
connect(widget, &MaterialWidget::modifyMaterial, this, &MaterialListWidget::modifyMaterial);
|
|
|
|
setItemWidget(item, col, widget);
|
|
|
|
widget->reload();
|
|
|
|
widget->updateCheckedState(isMaterialSelected(materialId));
|
|
|
|
m_itemMap[materialId] = std::make_pair(item, col);
|
|
|
|
}
|
|
|
|
invisibleRootItem()->addChild(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MaterialListWidget::setHasContextMenu(bool hasContextMenu)
|
|
|
|
{
|
|
|
|
m_hasContextMenu = hasContextMenu;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MaterialListWidget::removeAllContent()
|
|
|
|
{
|
|
|
|
m_itemMap.clear();
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MaterialListWidget::copy()
|
|
|
|
{
|
|
|
|
if (m_selectedMaterialIds.empty() && m_currentSelectedMaterialId.isNull())
|
|
|
|
return;
|
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
std::set<dust3d::Uuid> limitMaterialIds = m_selectedMaterialIds;
|
2018-10-09 02:19:12 +00:00
|
|
|
if (!m_currentSelectedMaterialId.isNull())
|
|
|
|
limitMaterialIds.insert(m_currentSelectedMaterialId);
|
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
std::set<dust3d::Uuid> emptySet;
|
2018-10-09 02:19:12 +00:00
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::Snapshot snapshot;
|
2018-10-25 00:19:38 +00:00
|
|
|
m_document->toSnapshot(&snapshot, emptySet, DocumentToSnapshotFor::Materials,
|
2021-11-18 14:58:01 +00:00
|
|
|
limitMaterialIds);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
std::string snapshotXml;
|
|
|
|
dust3d::saveSnapshotToXmlString(snapshot, snapshotXml);
|
2022-10-18 09:35:04 +00:00
|
|
|
QClipboard* clipboard = QApplication::clipboard();
|
2021-11-18 14:58:01 +00:00
|
|
|
clipboard->setText(snapshotXml.c_str());
|
2018-10-09 02:19:12 +00:00
|
|
|
}
|