Implement bone name edit and keep bone load order

master
Jeremy HU 2022-11-13 15:38:04 +11:00
parent 797ffd0b87
commit 5b6861be44
9 changed files with 113 additions and 6 deletions

View File

@ -4,6 +4,7 @@
#include "theme.h" #include "theme.h"
#include <QGroupBox> #include <QGroupBox>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLineEdit>
#include <QPushButton> #include <QPushButton>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <unordered_set> #include <unordered_set>
@ -19,8 +20,25 @@ BonePropertyWidget::BonePropertyWidget(Document* document,
QVBoxLayout* mainLayout = new QVBoxLayout; QVBoxLayout* mainLayout = new QVBoxLayout;
if (nullptr != m_bone) {
m_nameEdit = new QLineEdit;
Theme::initLineEdit(m_nameEdit);
m_nameEdit->setFixedWidth(Theme::partPreviewImageSize * 2.7);
m_nameEdit->setText(m_bone->name);
connect(m_nameEdit, &QLineEdit::textChanged, this, &BonePropertyWidget::nameEditChanged);
QHBoxLayout* renameLayout = new QHBoxLayout;
renameLayout->addWidget(new QLabel(tr("Name")));
renameLayout->addWidget(m_nameEdit);
renameLayout->addStretch();
mainLayout->addLayout(renameLayout);
}
mainLayout->setSizeConstraint(QLayout::SetFixedSize); mainLayout->setSizeConstraint(QLayout::SetFixedSize);
connect(this, &BonePropertyWidget::renameBone, m_document, &Document::renameBone);
connect(this, &BonePropertyWidget::groupOperationAdded, m_document, &Document::saveSnapshot); connect(this, &BonePropertyWidget::groupOperationAdded, m_document, &Document::saveSnapshot);
setLayout(mainLayout); setLayout(mainLayout);
@ -36,3 +54,12 @@ void BonePropertyWidget::prepareBoneIds()
m_boneId = m_boneIds.front(); m_boneId = m_boneIds.front();
} }
} }
void BonePropertyWidget::nameEditChanged()
{
const Document::Bone* bone = m_document->findBone(m_boneId);
if (nullptr == bone)
return;
emit renameBone(m_boneId, m_nameEdit->text());
emit groupOperationAdded();
}

View File

@ -5,22 +5,27 @@
#include <QWidget> #include <QWidget>
#include <dust3d/base/uuid.h> #include <dust3d/base/uuid.h>
class QLineEdit;
class BonePropertyWidget : public QWidget { class BonePropertyWidget : public QWidget {
Q_OBJECT Q_OBJECT
signals: signals:
void renameBone(const dust3d::Uuid& boneId, const QString& name);
void groupOperationAdded(); void groupOperationAdded();
public: public:
BonePropertyWidget(Document* document, BonePropertyWidget(Document* document,
const std::vector<dust3d::Uuid>& boneIds, const std::vector<dust3d::Uuid>& boneIds,
QWidget* parent = nullptr); QWidget* parent = nullptr);
public slots: private slots:
void nameEditChanged();
private: private:
Document* m_document = nullptr; Document* m_document = nullptr;
std::vector<dust3d::Uuid> m_boneIds; std::vector<dust3d::Uuid> m_boneIds;
dust3d::Uuid m_boneId; dust3d::Uuid m_boneId;
const Document::Bone* m_bone = nullptr; const Document::Bone* m_bone = nullptr;
QLineEdit* m_nameEdit = nullptr;
void prepareBoneIds(); void prepareBoneIds();
}; };

View File

@ -1876,9 +1876,11 @@ void Document::addFromSnapshot(const dust3d::Snapshot& snapshot, enum SnapshotSo
bone.name = dust3d::String::valueOrEmpty(boneKv.second, "name").c_str(); bone.name = dust3d::String::valueOrEmpty(boneKv.second, "name").c_str();
bone.attachBoneJointIndex = dust3d::String::toInt(dust3d::String::valueOrEmpty(boneKv.second, "attachBoneJointIndex")); bone.attachBoneJointIndex = dust3d::String::toInt(dust3d::String::valueOrEmpty(boneKv.second, "attachBoneJointIndex"));
boneMap.emplace(boneId, std::move(bone)); boneMap.emplace(boneId, std::move(bone));
boneIdList.push_back(boneId);
newAddedBoneIds.insert(boneId); newAddedBoneIds.insert(boneId);
} }
for (const auto& boneIdString : snapshot.boneIdList) {
boneIdList.push_back(oldNewIdMap[dust3d::Uuid(boneIdString)]);
}
for (const auto& boneKv : snapshot.bones) { for (const auto& boneKv : snapshot.bones) {
auto attachBoneId = dust3d::Uuid(dust3d::String::valueOrEmpty(boneKv.second, "attachBoneId")); auto attachBoneId = dust3d::Uuid(dust3d::String::valueOrEmpty(boneKv.second, "attachBoneId"));
if (!attachBoneId.isNull()) { if (!attachBoneId.isNull()) {

View File

@ -571,6 +571,9 @@ DocumentWindow::DocumentWindow()
connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setPartRoundState, m_document, &Document::setPartRoundState); connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setPartRoundState, m_document, &Document::setPartRoundState);
connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setPartWrapState, m_document, &Document::setPartCutRotation); connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setPartWrapState, m_document, &Document::setPartCutRotation);
connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::addNodesToBone, m_document, &Document::addNodesToBone);
connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::removeNodesFromBone, m_document, &Document::removeNodesFromBone);
connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setXlockState, m_document, &Document::setXlockState); connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setXlockState, m_document, &Document::setXlockState);
connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setYlockState, m_document, &Document::setYlockState); connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setYlockState, m_document, &Document::setYlockState);
connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setZlockState, m_document, &Document::setZlockState); connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setZlockState, m_document, &Document::setZlockState);

View File

@ -276,6 +276,27 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint& pos)
subMenu->addAction(&markAsNotBoneJointAction); subMenu->addAction(&markAsNotBoneJointAction);
} }
if (hasNodeSelection() && !m_document->boneIdList.empty()) {
QMenu* addToBoneMenu = contextMenu.addMenu(tr("Add To Bone"));
QMenu* removeFromBoneMenu = contextMenu.addMenu(tr("Remove From Bone"));
for (const auto& boneId : m_document->boneIdList) {
const Document::Bone* bone = m_document->findBone(boneId);
if (nullptr == bone)
continue;
QAction* addAction = new QAction(bone->previewPixmap, bone->name, this);
connect(addAction, &QAction::triggered, this, [=]() {
this->addSelectedToBone(boneId);
});
QAction* removeAction = new QAction(bone->previewPixmap, bone->name, this);
connect(removeAction, &QAction::triggered, this, [=]() {
this->removeSelectedFromBone(boneId);
});
addToBoneMenu->addAction(addAction);
removeFromBoneMenu->addAction(removeAction);
}
}
QAction selectAllAction(tr("Select All"), this); QAction selectAllAction(tr("Select All"), this);
if (hasItems()) { if (hasItems()) {
connect(&selectAllAction, &QAction::triggered, this, &SkeletonGraphicsWidget::selectAll); connect(&selectAllAction, &QAction::triggered, this, &SkeletonGraphicsWidget::selectAll);
@ -2935,3 +2956,33 @@ void SkeletonGraphicsWidget::markSelectedAsNotBoneJoint()
setNodeBoneJointStates(nodeIds, false); setNodeBoneJointStates(nodeIds, false);
} }
void SkeletonGraphicsWidget::addSelectedToBone(const dust3d::Uuid& boneId)
{
std::set<SkeletonGraphicsNodeItem*> nodeItems;
readMergedSkeletonNodeSetFromRangeSelection(&nodeItems);
if (nodeItems.empty())
return;
std::vector<dust3d::Uuid> nodeIds;
for (const auto& it : nodeItems) {
nodeIds.push_back(it->id());
}
emit addNodesToBone(boneId, nodeIds);
}
void SkeletonGraphicsWidget::removeSelectedFromBone(const dust3d::Uuid& boneId)
{
std::set<SkeletonGraphicsNodeItem*> nodeItems;
readMergedSkeletonNodeSetFromRangeSelection(&nodeItems);
if (nodeItems.empty())
return;
std::vector<dust3d::Uuid> nodeIds;
for (const auto& it : nodeItems) {
nodeIds.push_back(it->id());
}
emit removeNodesFromBone(boneId, nodeIds);
}

View File

@ -30,6 +30,8 @@ signals:
void scaleNodeByAddRadius(dust3d::Uuid nodeId, float amount); void scaleNodeByAddRadius(dust3d::Uuid nodeId, float amount);
void moveNodeBy(dust3d::Uuid nodeId, float x, float y, float z); void moveNodeBy(dust3d::Uuid nodeId, float x, float y, float z);
void setNodeBoneJointState(const dust3d::Uuid& nodeId, bool boneJoint); void setNodeBoneJointState(const dust3d::Uuid& nodeId, bool boneJoint);
void addNodesToBone(const dust3d::Uuid& boneId, const std::vector<dust3d::Uuid>& nodeIds);
void removeNodesFromBone(const dust3d::Uuid& boneId, const std::vector<dust3d::Uuid>& nodeIds);
void removeNode(dust3d::Uuid nodeId); void removeNode(dust3d::Uuid nodeId);
void removePart(dust3d::Uuid partId); void removePart(dust3d::Uuid partId);
void setEditMode(Document::EditMode mode); void setEditMode(Document::EditMode mode);
@ -236,6 +238,8 @@ private:
void rotateAllSideProfile(int degree); void rotateAllSideProfile(int degree);
bool isFloatEqual(float a, float b); bool isFloatEqual(float a, float b);
void setNodeBoneJointStates(const std::vector<dust3d::Uuid>& nodeIds, bool boneJoint); void setNodeBoneJointStates(const std::vector<dust3d::Uuid>& nodeIds, bool boneJoint);
void addSelectedToBone(const dust3d::Uuid& boneId);
void removeSelectedFromBone(const dust3d::Uuid& boneId);
private: private:
const Document* m_document = nullptr; const Document* m_document = nullptr;

View File

@ -163,3 +163,8 @@ void Theme::initIconButton(QPushButton* button)
{ {
button->setFixedSize(Theme::toolIconSize, Theme::toolIconSize); button->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
} }
void Theme::initLineEdit(QLineEdit* edit)
{
edit->setStyleSheet("QLineEdit { background-color: black }");
}

View File

@ -6,6 +6,7 @@
#include <QCheckBox> #include <QCheckBox>
#include <QColor> #include <QColor>
#include <QLabel> #include <QLabel>
#include <QLineEdit>
#include <QPushButton> #include <QPushButton>
class Theme { class Theme {
@ -42,6 +43,7 @@ public:
static void initToolButton(QPushButton* button); static void initToolButton(QPushButton* button);
static void initCheckbox(QCheckBox* checkbox); static void initCheckbox(QCheckBox* checkbox);
static void initIconButton(QPushButton* button); static void initIconButton(QPushButton* button);
static void initLineEdit(QLineEdit* edit);
}; };
#endif #endif

View File

@ -115,6 +115,12 @@ void loadSnapshotFromXmlString(Snapshot* snapshot, char* xmlString)
} }
} }
} }
for (rapidxml::xml_node<>* node = bones->first_node(); nullptr != node; node = node->next_sibling()) {
rapidxml::xml_attribute<>* idAttribute = node->first_attribute("id");
if (nullptr != idAttribute) {
snapshot->boneIdList.push_back(idAttribute->value());
}
}
} }
rapidxml::xml_node<>* partIdList = canvas->first_node("partIdList"); rapidxml::xml_node<>* partIdList = canvas->first_node("partIdList");
@ -232,13 +238,15 @@ void saveSnapshotToXmlString(const Snapshot& snapshot, std::string& xmlString)
} }
xmlString += " </parts>\n"; xmlString += " </parts>\n";
if (!snapshot.bones.empty()) { if (!snapshot.boneIdList.empty()) {
xmlString += " <bones>\n"; xmlString += " <bones>\n";
std::map<std::string, std::map<std::string, std::string>>::const_iterator boneIterator; for (const auto& boneId : snapshot.boneIdList) {
for (boneIterator = snapshot.bones.begin(); boneIterator != snapshot.bones.end(); boneIterator++) { auto findBone = snapshot.bones.find(boneId);
if (findBone == snapshot.bones.end())
continue;
std::map<std::string, std::string>::const_iterator boneAttributeIterator; std::map<std::string, std::string>::const_iterator boneAttributeIterator;
xmlString += " <bone"; xmlString += " <bone";
for (boneAttributeIterator = boneIterator->second.begin(); boneAttributeIterator != boneIterator->second.end(); boneAttributeIterator++) { for (boneAttributeIterator = findBone->second.begin(); boneAttributeIterator != findBone->second.end(); boneAttributeIterator++) {
if (String::startsWith(boneAttributeIterator->first, "__")) if (String::startsWith(boneAttributeIterator->first, "__"))
continue; continue;
xmlString += " " + boneAttributeIterator->first + "=\"" + boneAttributeIterator->second + "\""; xmlString += " " + boneAttributeIterator->first + "=\"" + boneAttributeIterator->second + "\"";