diff --git a/application/sources/bone_property_widget.cc b/application/sources/bone_property_widget.cc index 63900191..be19a7df 100644 --- a/application/sources/bone_property_widget.cc +++ b/application/sources/bone_property_widget.cc @@ -4,6 +4,7 @@ #include "theme.h" #include #include +#include #include #include #include @@ -19,8 +20,25 @@ BonePropertyWidget::BonePropertyWidget(Document* document, 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); + connect(this, &BonePropertyWidget::renameBone, m_document, &Document::renameBone); connect(this, &BonePropertyWidget::groupOperationAdded, m_document, &Document::saveSnapshot); setLayout(mainLayout); @@ -36,3 +54,12 @@ void BonePropertyWidget::prepareBoneIds() 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(); +} diff --git a/application/sources/bone_property_widget.h b/application/sources/bone_property_widget.h index d83d92b1..6b0c17bb 100644 --- a/application/sources/bone_property_widget.h +++ b/application/sources/bone_property_widget.h @@ -5,22 +5,27 @@ #include #include +class QLineEdit; + class BonePropertyWidget : public QWidget { Q_OBJECT signals: + void renameBone(const dust3d::Uuid& boneId, const QString& name); void groupOperationAdded(); public: BonePropertyWidget(Document* document, const std::vector& boneIds, QWidget* parent = nullptr); -public slots: +private slots: + void nameEditChanged(); private: Document* m_document = nullptr; std::vector m_boneIds; dust3d::Uuid m_boneId; const Document::Bone* m_bone = nullptr; + QLineEdit* m_nameEdit = nullptr; void prepareBoneIds(); }; diff --git a/application/sources/document.cc b/application/sources/document.cc index 7bd4ca9d..99d80f2a 100644 --- a/application/sources/document.cc +++ b/application/sources/document.cc @@ -1876,9 +1876,11 @@ void Document::addFromSnapshot(const dust3d::Snapshot& snapshot, enum SnapshotSo bone.name = dust3d::String::valueOrEmpty(boneKv.second, "name").c_str(); bone.attachBoneJointIndex = dust3d::String::toInt(dust3d::String::valueOrEmpty(boneKv.second, "attachBoneJointIndex")); boneMap.emplace(boneId, std::move(bone)); - boneIdList.push_back(boneId); newAddedBoneIds.insert(boneId); } + for (const auto& boneIdString : snapshot.boneIdList) { + boneIdList.push_back(oldNewIdMap[dust3d::Uuid(boneIdString)]); + } for (const auto& boneKv : snapshot.bones) { auto attachBoneId = dust3d::Uuid(dust3d::String::valueOrEmpty(boneKv.second, "attachBoneId")); if (!attachBoneId.isNull()) { diff --git a/application/sources/document_window.cc b/application/sources/document_window.cc index 746f1dfd..7f7184e4 100644 --- a/application/sources/document_window.cc +++ b/application/sources/document_window.cc @@ -571,6 +571,9 @@ DocumentWindow::DocumentWindow() connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setPartRoundState, m_document, &Document::setPartRoundState); 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::setYlockState, m_document, &Document::setYlockState); connect(canvasGraphicsWidget, &SkeletonGraphicsWidget::setZlockState, m_document, &Document::setZlockState); diff --git a/application/sources/skeleton_graphics_widget.cc b/application/sources/skeleton_graphics_widget.cc index 2900066a..bfcc414f 100644 --- a/application/sources/skeleton_graphics_widget.cc +++ b/application/sources/skeleton_graphics_widget.cc @@ -276,6 +276,27 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint& pos) 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); if (hasItems()) { connect(&selectAllAction, &QAction::triggered, this, &SkeletonGraphicsWidget::selectAll); @@ -2935,3 +2956,33 @@ void SkeletonGraphicsWidget::markSelectedAsNotBoneJoint() setNodeBoneJointStates(nodeIds, false); } + +void SkeletonGraphicsWidget::addSelectedToBone(const dust3d::Uuid& boneId) +{ + std::set nodeItems; + readMergedSkeletonNodeSetFromRangeSelection(&nodeItems); + if (nodeItems.empty()) + return; + + std::vector nodeIds; + for (const auto& it : nodeItems) { + nodeIds.push_back(it->id()); + } + + emit addNodesToBone(boneId, nodeIds); +} + +void SkeletonGraphicsWidget::removeSelectedFromBone(const dust3d::Uuid& boneId) +{ + std::set nodeItems; + readMergedSkeletonNodeSetFromRangeSelection(&nodeItems); + if (nodeItems.empty()) + return; + + std::vector nodeIds; + for (const auto& it : nodeItems) { + nodeIds.push_back(it->id()); + } + + emit removeNodesFromBone(boneId, nodeIds); +} diff --git a/application/sources/skeleton_graphics_widget.h b/application/sources/skeleton_graphics_widget.h index c915f7de..39e67ab5 100644 --- a/application/sources/skeleton_graphics_widget.h +++ b/application/sources/skeleton_graphics_widget.h @@ -30,6 +30,8 @@ signals: void scaleNodeByAddRadius(dust3d::Uuid nodeId, float amount); void moveNodeBy(dust3d::Uuid nodeId, float x, float y, float z); void setNodeBoneJointState(const dust3d::Uuid& nodeId, bool boneJoint); + void addNodesToBone(const dust3d::Uuid& boneId, const std::vector& nodeIds); + void removeNodesFromBone(const dust3d::Uuid& boneId, const std::vector& nodeIds); void removeNode(dust3d::Uuid nodeId); void removePart(dust3d::Uuid partId); void setEditMode(Document::EditMode mode); @@ -236,6 +238,8 @@ private: void rotateAllSideProfile(int degree); bool isFloatEqual(float a, float b); void setNodeBoneJointStates(const std::vector& nodeIds, bool boneJoint); + void addSelectedToBone(const dust3d::Uuid& boneId); + void removeSelectedFromBone(const dust3d::Uuid& boneId); private: const Document* m_document = nullptr; diff --git a/application/sources/theme.cc b/application/sources/theme.cc index 1a7ae801..3fe73075 100644 --- a/application/sources/theme.cc +++ b/application/sources/theme.cc @@ -163,3 +163,8 @@ void Theme::initIconButton(QPushButton* button) { button->setFixedSize(Theme::toolIconSize, Theme::toolIconSize); } + +void Theme::initLineEdit(QLineEdit* edit) +{ + edit->setStyleSheet("QLineEdit { background-color: black }"); +} diff --git a/application/sources/theme.h b/application/sources/theme.h index 124d0046..6e2965e5 100644 --- a/application/sources/theme.h +++ b/application/sources/theme.h @@ -6,6 +6,7 @@ #include #include #include +#include #include class Theme { @@ -42,6 +43,7 @@ public: static void initToolButton(QPushButton* button); static void initCheckbox(QCheckBox* checkbox); static void initIconButton(QPushButton* button); + static void initLineEdit(QLineEdit* edit); }; #endif diff --git a/dust3d/base/snapshot_xml.cc b/dust3d/base/snapshot_xml.cc index ddbf05d6..f2a6e692 100644 --- a/dust3d/base/snapshot_xml.cc +++ b/dust3d/base/snapshot_xml.cc @@ -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"); @@ -232,13 +238,15 @@ void saveSnapshotToXmlString(const Snapshot& snapshot, std::string& xmlString) } xmlString += " \n"; - if (!snapshot.bones.empty()) { + if (!snapshot.boneIdList.empty()) { xmlString += " \n"; - std::map>::const_iterator boneIterator; - for (boneIterator = snapshot.bones.begin(); boneIterator != snapshot.bones.end(); boneIterator++) { + for (const auto& boneId : snapshot.boneIdList) { + auto findBone = snapshot.bones.find(boneId); + if (findBone == snapshot.bones.end()) + continue; std::map::const_iterator boneAttributeIterator; xmlString += " second.begin(); boneAttributeIterator != boneIterator->second.end(); boneAttributeIterator++) { + for (boneAttributeIterator = findBone->second.begin(); boneAttributeIterator != findBone->second.end(); boneAttributeIterator++) { if (String::startsWith(boneAttributeIterator->first, "__")) continue; xmlString += " " + boneAttributeIterator->first + "=\"" + boneAttributeIterator->second + "\"";