Add undo/redo

master
Jeremy Hu 2018-04-09 07:34:46 +08:00
parent 7b3540f0c1
commit 35a62960ca
5 changed files with 84 additions and 13 deletions

View File

@ -5,6 +5,8 @@
#include "skeletondocument.h"
#include "util.h"
unsigned long SkeletonDocument::m_maxSnapshot = 1000;
SkeletonDocument::SkeletonDocument() :
m_resultMeshIsObsolete(false),
m_resultMesh(nullptr),
@ -33,7 +35,7 @@ void SkeletonDocument::removeEdge(QUuid edgeId)
qDebug() << "Find edge failed:" << edgeId;
return;
}
if (isPartLocked(edge->partId))
if (isPartReadonly(edge->partId))
return;
const SkeletonPart *oldPart = findPart(edge->partId);
if (nullptr == oldPart) {
@ -94,7 +96,7 @@ void SkeletonDocument::removeNode(QUuid nodeId)
qDebug() << "Find node failed:" << nodeId;
return;
}
if (isPartLocked(node->partId))
if (isPartReadonly(node->partId))
return;
const SkeletonPart *oldPart = findPart(node->partId);
if (nullptr == oldPart) {
@ -174,7 +176,7 @@ void SkeletonDocument::addNode(float x, float y, float z, float radius, QUuid fr
return;
}
partId = fromNode->partId;
if (isPartLocked(partId))
if (isPartReadonly(partId))
return;
}
SkeletonNode node;
@ -243,7 +245,7 @@ void SkeletonDocument::addEdge(QUuid fromNodeId, QUuid toNodeId)
return;
}
if (isPartLocked(fromNode->partId))
if (isPartReadonly(fromNode->partId))
return;
toNode = findNode(toNodeId);
@ -252,7 +254,7 @@ void SkeletonDocument::addEdge(QUuid fromNodeId, QUuid toNodeId)
return;
}
if (isPartLocked(toNode->partId))
if (isPartReadonly(toNode->partId))
return;
QUuid toPartId = toNode->partId;
@ -339,21 +341,21 @@ void SkeletonDocument::scaleNodeByAddRadius(QUuid nodeId, float amount)
qDebug() << "Find node failed:" << nodeId;
return;
}
if (isPartLocked(it->second.partId))
if (isPartReadonly(it->second.partId))
return;
it->second.radius += amount;
emit nodeRadiusChanged(nodeId);
emit skeletonChanged();
}
bool SkeletonDocument::isPartLocked(QUuid partId)
bool SkeletonDocument::isPartReadonly(QUuid partId)
{
const SkeletonPart *part = findPart(partId);
if (nullptr == part) {
qDebug() << "Find part failed:" << partId;
return true;
}
return part->locked;
return part->locked || !part->visible;
}
void SkeletonDocument::moveNodeBy(QUuid nodeId, float x, float y, float z)
@ -363,7 +365,7 @@ void SkeletonDocument::moveNodeBy(QUuid nodeId, float x, float y, float z)
qDebug() << "Find node failed:" << nodeId;
return;
}
if (isPartLocked(it->second.partId))
if (isPartReadonly(it->second.partId))
return;
it->second.x += x;
it->second.y += y;
@ -379,7 +381,7 @@ void SkeletonDocument::setNodeOrigin(QUuid nodeId, float x, float y, float z)
qDebug() << "Find node failed:" << nodeId;
return;
}
if (isPartLocked(it->second.partId))
if (isPartReadonly(it->second.partId))
return;
it->second.x = x;
it->second.y = y;
@ -395,7 +397,7 @@ void SkeletonDocument::setNodeRadius(QUuid nodeId, float radius)
qDebug() << "Find node failed:" << nodeId;
return;
}
if (isPartLocked(it->second.partId))
if (isPartReadonly(it->second.partId))
return;
it->second.radius = radius;
emit nodeRadiusChanged(nodeId);
@ -555,6 +557,8 @@ void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
nodeMap.clear();
edgeMap.clear();
partMap.clear();
partIds.clear();
emit partListChanged();
for (const auto &nodeKv : snapshot.nodes) {
SkeletonNode node(QUuid(nodeKv.first));
@ -603,6 +607,7 @@ void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
}
emit partListChanged();
emit skeletonChanged();
}
const char *SkeletonNodeRootMarkModeToString(SkeletonNodeRootMarkMode mode)
@ -772,3 +777,33 @@ void SkeletonDocument::setPartSubdivState(QUuid partId, bool subdived)
emit skeletonChanged();
}
void SkeletonDocument::saveSnapshot()
{
if (m_undoItems.size() + 1 > m_maxSnapshot)
m_undoItems.pop_front();
SkeletonHistoryItem item;
toSnapshot(&item.snapshot);
m_undoItems.push_back(item);
qDebug() << "Undo items:" << m_undoItems.size();
}
void SkeletonDocument::undo()
{
if (m_undoItems.empty())
return;
m_redoItems.push_back(m_undoItems.back());
fromSnapshot(m_undoItems.back().snapshot);
m_undoItems.pop_back();
qDebug() << "Undo/Redo items:" << m_undoItems.size() << m_redoItems.size();
}
void SkeletonDocument::redo()
{
if (m_redoItems.empty())
return;
m_undoItems.push_back(m_redoItems.back());
fromSnapshot(m_redoItems.back().snapshot);
m_redoItems.pop_back();
qDebug() << "Undo/Redo items:" << m_undoItems.size() << m_redoItems.size();
}

View File

@ -5,6 +5,7 @@
#include <vector>
#include <map>
#include <set>
#include <deque>
#include <QImage>
#include "skeletonsnapshot.h"
#include "mesh.h"
@ -148,7 +149,6 @@ public:
std::map<QUuid, SkeletonPart> partMap;
std::map<QUuid, SkeletonNode> nodeMap;
std::map<QUuid, SkeletonEdge> edgeMap;
std::vector<SkeletonHistoryItem> historyItems;
std::vector<QUuid> partIds;
QImage turnaround;
SkeletonDocumentEditMode editMode;
@ -180,15 +180,21 @@ public slots:
void setPartLockState(QUuid partId, bool locked);
void setPartVisibleState(QUuid partId, bool visible);
void setPartSubdivState(QUuid partId, bool subdived);
void saveSnapshot();
void undo();
void redo();
private:
void splitPartByNode(std::vector<std::vector<QUuid>> *groups, QUuid nodeId);
void joinNodeAndNeiborsToGroup(std::vector<QUuid> *group, QUuid nodeId, std::set<QUuid> *visitMap, QUuid noUseEdgeId=QUuid());
void splitPartByEdge(std::vector<std::vector<QUuid>> *groups, QUuid edgeId);
bool isPartLocked(QUuid partId);
bool isPartReadonly(QUuid partId);
private:
bool m_resultMeshIsObsolete;
MeshGenerator *m_meshGenerator;
Mesh *m_resultMesh;
static unsigned long m_maxSnapshot;
std::deque<SkeletonHistoryItem> m_undoItems;
std::deque<SkeletonHistoryItem> m_redoItems;
};
#endif

View File

@ -157,6 +157,10 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(changeTurnaroundButton, &QPushButton::clicked, this, &SkeletonDocumentWindow::changeTurnaround);
connect(undoButton, &QPushButton::clicked, [=]() {
m_document->undo();
});
connect(addButton, &QPushButton::clicked, [=]() {
m_document->setEditMode(SkeletonDocumentEditMode::Add);
});
@ -186,6 +190,9 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(graphicsWidget, &SkeletonGraphicsWidget::setEditMode, m_document, &SkeletonDocument::setEditMode);
connect(graphicsWidget, &SkeletonGraphicsWidget::removeEdge, m_document, &SkeletonDocument::removeEdge);
connect(graphicsWidget, &SkeletonGraphicsWidget::addEdge, m_document, &SkeletonDocument::addEdge);
connect(graphicsWidget, &SkeletonGraphicsWidget::groupOperationAdded, m_document, &SkeletonDocument::saveSnapshot);
connect(graphicsWidget, &SkeletonGraphicsWidget::undo, m_document, &SkeletonDocument::undo);
connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_document, &SkeletonDocument::redo);
connect(m_document, &SkeletonDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded);
connect(m_document, &SkeletonDocument::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved);

View File

@ -359,10 +359,12 @@ bool SkeletonGraphicsWidget::wheel(QWheelEvent *event)
emit scaleNodeByAddRadius(nodeItem->id(), unifiedDelta);
}
}
emit groupOperationAdded();
return true;
} else if (m_hoveredNodeItem) {
float unifiedDelta = sceneRadiusToUnified(delta);
emit scaleNodeByAddRadius(m_hoveredNodeItem->id(), unifiedDelta);
emit groupOperationAdded();
return true;
}
}
@ -379,6 +381,7 @@ bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
}
if (m_moveStarted) {
m_moveStarted = false;
emit groupOperationAdded();
}
if (m_rangeSelectionStarted) {
m_selectionItem->hide();
@ -428,6 +431,7 @@ bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
if (m_document->findEdgeByNodes(m_addFromNodeItem->id(), m_hoveredNodeItem->id()))
return true;
emit addEdge(m_addFromNodeItem->id(), m_hoveredNodeItem->id());
emit groupOperationAdded();
return true;
}
}
@ -452,6 +456,7 @@ bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
m_lastAddedZ = unifiedSidePos.x();
qDebug() << "Emit add node " << m_lastAddedX << m_lastAddedY << m_lastAddedZ;
emit addNode(unifiedMainPos.x(), unifiedMainPos.y(), unifiedSidePos.x(), sceneRadiusToUnified(m_cursorNodeItem->radius()), nullptr == m_addFromNodeItem ? QUuid() : m_addFromNodeItem->id());
emit groupOperationAdded();
return true;
}
} else if (SkeletonDocumentEditMode::Select == m_document->editMode) {
@ -533,6 +538,7 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
}
}
if (processed) {
emit groupOperationAdded();
return true;
}
} else if (event->key() == Qt::Key_A) {
@ -542,6 +548,20 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
emit setEditMode(SkeletonDocumentEditMode::Add);
}
return true;
} else if (event->key() == Qt::Key_Z) {
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) {
emit redo();
} else {
emit undo();
}
}
} else if (event->key() == Qt::Key_Y) {
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
if (!QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) {
emit redo();
}
}
}
return false;
}

View File

@ -259,6 +259,9 @@ signals:
void removeEdge(QUuid edgeId);
void addEdge(QUuid fromNodeId, QUuid toNodeId);
void cursorChanged();
void groupOperationAdded();
void undo();
void redo();
public:
SkeletonGraphicsWidget(const SkeletonDocument *document);
std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap;