diff --git a/src/main.cpp b/src/main.cpp index aa6fc1d3..5606e957 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,6 +32,7 @@ int main(int argc, char ** argv) darkPalette.setColor(QPalette::ToolTipBase, Theme::white); darkPalette.setColor(QPalette::ToolTipText, Theme::white); darkPalette.setColor(QPalette::Text, Theme::white); + darkPalette.setColor(QPalette::Disabled, QPalette::Text, Theme::black); darkPalette.setColor(QPalette::Button, QColor(53,53,53)); darkPalette.setColor(QPalette::ButtonText, Theme::white); darkPalette.setColor(QPalette::BrightText, Theme::red); diff --git a/src/skeletondocument.cpp b/src/skeletondocument.cpp index da24ed7d..a7235880 100644 --- a/src/skeletondocument.cpp +++ b/src/skeletondocument.cpp @@ -14,7 +14,8 @@ unsigned long SkeletonDocument::m_maxSnapshot = 1000; SkeletonDocument::SkeletonDocument() : m_resultMeshIsObsolete(false), m_resultMesh(nullptr), - m_meshGenerator(nullptr) + m_meshGenerator(nullptr), + m_batchChangeRefCount(0) { } @@ -754,9 +755,24 @@ void SkeletonDocument::meshReady() } } +void SkeletonDocument::batchChangeBegin() +{ + m_batchChangeRefCount++; +} + +void SkeletonDocument::batchChangeEnd() +{ + m_batchChangeRefCount--; + if (0 == m_batchChangeRefCount) { + if (m_resultMeshIsObsolete) { + generateMesh(); + } + } +} + void SkeletonDocument::generateMesh() { - if (nullptr != m_meshGenerator) { + if (nullptr != m_meshGenerator || m_batchChangeRefCount > 0) { m_resultMeshIsObsolete = true; return; } @@ -867,3 +883,15 @@ void SkeletonDocument::paste() addFromSnapshot(snapshot); } } + +bool SkeletonDocument::hasPastableContentInClipboard() const +{ + const QClipboard *clipboard = QApplication::clipboard(); + const QMimeData *mimeData = clipboard->mimeData(); + if (mimeData->hasText()) { + if (-1 != mimeData->text().left(1000).indexOf("partIdList")) + return true; + } + return false; +} + diff --git a/src/skeletondocument.h b/src/skeletondocument.h index 90e56633..b7d0a4b3 100644 --- a/src/skeletondocument.h +++ b/src/skeletondocument.h @@ -106,6 +106,13 @@ public: } }; +enum class SkeletonProfile +{ + Unknown = 0, + Main, + Side +}; + class SkeletonHistoryItem { public: @@ -121,13 +128,6 @@ enum class SkeletonDocumentEditMode ZoomOut }; -enum class SkeletonProfile -{ - Unknown = 0, - Main, - Side -}; - class SkeletonDocument : public QObject { Q_OBJECT @@ -170,6 +170,7 @@ public: Mesh *takeResultMesh(); QImage preview; void updateTurnaround(const QImage &image); + bool hasPastableContentInClipboard() const; public slots: void removeNode(QUuid nodeId); void removeEdge(QUuid edgeId); @@ -193,6 +194,8 @@ public slots: void undo(); void redo(); void paste(); + void batchChangeBegin(); + void batchChangeEnd(); private: void splitPartByNode(std::vector> *groups, QUuid nodeId); void joinNodeAndNeiborsToGroup(std::vector *group, QUuid nodeId, std::set *visitMap, QUuid noUseEdgeId=QUuid()); @@ -205,6 +208,7 @@ private: static unsigned long m_maxSnapshot; std::deque m_undoItems; std::deque m_redoItems; + int m_batchChangeRefCount; }; #endif diff --git a/src/skeletondocumentwindow.cpp b/src/skeletondocumentwindow.cpp index 4bafbd60..16deac23 100644 --- a/src/skeletondocumentwindow.cpp +++ b/src/skeletondocumentwindow.cpp @@ -195,6 +195,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_document, &SkeletonDocument::redo); connect(graphicsWidget, &SkeletonGraphicsWidget::paste, m_document, &SkeletonDocument::paste); connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &SkeletonDocumentWindow::changeTurnaround); + connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeBegin, m_document, &SkeletonDocument::batchChangeBegin); + connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeEnd, m_document, &SkeletonDocument::batchChangeEnd); connect(m_document, &SkeletonDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded); connect(m_document, &SkeletonDocument::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved); diff --git a/src/skeletongraphicswidget.cpp b/src/skeletongraphicswidget.cpp index ac789e6e..6ee38c71 100644 --- a/src/skeletongraphicswidget.cpp +++ b/src/skeletongraphicswidget.cpp @@ -102,10 +102,23 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos) QAction pasteAction("Paste", this); connect(&pasteAction, &QAction::triggered, m_document, &SkeletonDocument::paste); + pasteAction.setEnabled(m_document->hasPastableContentInClipboard()); contextMenu.addAction(&pasteAction); contextMenu.addSeparator(); + QAction flipHorizontallyAction("H Flip", this); + connect(&flipHorizontallyAction, &QAction::triggered, this, &SkeletonGraphicsWidget::flipHorizontally); + flipHorizontallyAction.setEnabled(!m_rangeSelectionSet.empty()); + contextMenu.addAction(&flipHorizontallyAction); + + QAction flipVerticallyAction("V Flip", this); + connect(&flipVerticallyAction, &QAction::triggered, this, &SkeletonGraphicsWidget::flipVertically); + flipVerticallyAction.setEnabled(!m_rangeSelectionSet.empty()); + contextMenu.addAction(&flipVerticallyAction); + + contextMenu.addSeparator(); + QAction selectAllAction("Select All", this); connect(&selectAllAction, &QAction::triggered, this, &SkeletonGraphicsWidget::selectAll); selectAllAction.setEnabled(!nodeItemMap.empty()); @@ -123,18 +136,6 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos) contextMenu.addSeparator(); - QAction addMirrorAction("Mirror", this); - connect(&addMirrorAction, &QAction::triggered, this, &SkeletonGraphicsWidget::addMirror); - addMirrorAction.setEnabled(readSkeletonNodeAndAnyEdgeOfNodeFromRangeSelection(nullptr, nullptr)); - contextMenu.addAction(&addMirrorAction); - - QAction deleteMirrorAction("Delete Mirror", this); - connect(&deleteMirrorAction, &QAction::triggered, this, &SkeletonGraphicsWidget::deleteMirror); - deleteMirrorAction.setEnabled(readSkeletonNodeAndAnyEdgeOfNodeFromRangeSelection(nullptr, nullptr)); - contextMenu.addAction(&deleteMirrorAction); - - contextMenu.addSeparator(); - QAction changeTurnaroundAction("Change Turnaround..", this); connect(&changeTurnaroundAction, &QAction::triggered, [=]() { emit changeTurnaround(); @@ -425,11 +426,7 @@ bool SkeletonGraphicsWidget::wheel(QWheelEvent *event) readMergedSkeletonNodeSetFromRangeSelection(&nodeItems); float unifiedDelta = sceneRadiusToUnified(delta); if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) { - QVector2D center; - for (const auto &nodeItem: nodeItems) { - center += QVector2D(nodeItem->origin()); - } - center /= nodeItems.size(); + QVector2D center = centerOfNodeItemSet(nodeItems); for (const auto &nodeItem: nodeItems) { QVector2D origin = QVector2D(nodeItem->origin()); QVector2D ray = (center - origin) * 0.01 * delta; @@ -459,6 +456,50 @@ bool SkeletonGraphicsWidget::wheel(QWheelEvent *event) return false; } +QVector2D SkeletonGraphicsWidget::centerOfNodeItemSet(const std::set &set) +{ + QVector2D center; + for (const auto &nodeItem: set) { + center += QVector2D(nodeItem->origin()); + } + center /= set.size(); + return center; +} + +void SkeletonGraphicsWidget::flipHorizontally() +{ + std::set nodeItems; + readMergedSkeletonNodeSetFromRangeSelection(&nodeItems); + if (nodeItems.empty()) + return; + QVector2D center = centerOfNodeItemSet(nodeItems); + for (const auto &nodeItem: nodeItems) { + QPointF origin = nodeItem->origin(); + float offset = origin.x() - center.x(); + float unifiedOffset = -sceneRadiusToUnified(offset * 2); + if (SkeletonProfile::Main == nodeItem->profile()) { + emit moveNodeBy(nodeItem->id(), unifiedOffset, 0, 0); + } else { + emit moveNodeBy(nodeItem->id(), 0, 0, unifiedOffset); + } + } +} + +void SkeletonGraphicsWidget::flipVertically() +{ + std::set nodeItems; + readMergedSkeletonNodeSetFromRangeSelection(&nodeItems); + if (nodeItems.empty()) + return; + QVector2D center = centerOfNodeItemSet(nodeItems); + for (const auto &nodeItem: nodeItems) { + QPointF origin = nodeItem->origin(); + float offset = origin.y() - center.y(); + float unifiedOffset = -sceneRadiusToUnified(offset * 2); + emit moveNodeBy(nodeItem->id(), 0, unifiedOffset, 0); + } +} + bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { @@ -619,6 +660,7 @@ bool SkeletonGraphicsWidget::mouseDoubleClick(QMouseEvent *event) void SkeletonGraphicsWidget::deleteSelected() { if (!m_rangeSelectionSet.empty()) { + emit batchChangeBegin(); std::set nodeIdSet; std::set edgeIdSet; readSkeletonNodeAndEdgeIdSetFromRangeSelection(&nodeIdSet, &edgeIdSet); @@ -628,6 +670,7 @@ void SkeletonGraphicsWidget::deleteSelected() for (const auto &id: nodeIdSet) { emit removeNode(id); } + emit batchChangeEnd(); } } @@ -1094,3 +1137,6 @@ void SkeletonGraphicsWidget::copy() QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(snapshotXml); } + + + diff --git a/src/skeletongraphicswidget.h b/src/skeletongraphicswidget.h index bddca0a3..f54f9967 100644 --- a/src/skeletongraphicswidget.h +++ b/src/skeletongraphicswidget.h @@ -264,8 +264,8 @@ signals: void redo(); void paste(); void changeTurnaround(); - void addMirror(); - void deleteMirror(); + void batchChangeBegin(); + void batchChangeEnd(); public: SkeletonGraphicsWidget(const SkeletonDocument *document); std::map> nodeItemMap; @@ -308,6 +308,8 @@ public slots: void selectPartAll(); void cut(); void copy(); + void flipHorizontally(); + void flipVertically(); private slots: void turnaroundImageReady(); private: @@ -321,6 +323,7 @@ private: void checkRangeSelection(); void clearRangeSelection(); void removeItem(QGraphicsItem *item); + QVector2D centerOfNodeItemSet(const std::set &set); private: QGraphicsPixmapItem *m_backgroundItem; const SkeletonDocument *m_document; diff --git a/src/skeletonsnapshot.h b/src/skeletonsnapshot.h index b94974dc..f6e5b850 100644 --- a/src/skeletonsnapshot.h +++ b/src/skeletonsnapshot.h @@ -13,6 +13,8 @@ public: std::map> nodes; std::map> edges; std::map> parts; + std::map> mirrors; + std::map> trivialMarkers; std::vector partIdList; public: SkeletonSnapshot();