From f6f36f4f2e9a2081f811c4188dcaad58fafa8a82 Mon Sep 17 00:00:00 2001 From: parax0 Date: Sun, 31 Jan 2016 14:25:16 -0700 Subject: [PATCH] Support for undo/redo on array resize --- src/Core/Resource/Script/IProperty.cpp | 13 +++ src/Core/Resource/Script/IProperty.h | 36 ++++++++ src/Editor/Editor.pro | 8 +- src/Editor/PropertyEdit/CPropertyDelegate.cpp | 18 +++- src/Editor/PropertyEdit/CPropertyModel.cpp | 32 ++++--- src/Editor/PropertyEdit/CPropertyModel.h | 3 +- src/Editor/Undo/CBasicPropertyCommand.cpp | 87 +++++++++++++++++++ src/Editor/Undo/CBasicPropertyCommand.h | 23 +++++ .../Undo/CEditScriptPropertyCommand.cpp | 7 +- src/Editor/Undo/CEditScriptPropertyCommand.h | 9 +- src/Editor/Undo/CResizeScriptArrayCommand.cpp | 74 ++++++++++++++++ src/Editor/Undo/CResizeScriptArrayCommand.h | 25 ++++++ src/Editor/Undo/UndoCommands.h | 1 + 13 files changed, 306 insertions(+), 30 deletions(-) create mode 100644 src/Editor/Undo/CBasicPropertyCommand.cpp create mode 100644 src/Editor/Undo/CBasicPropertyCommand.h create mode 100644 src/Editor/Undo/CResizeScriptArrayCommand.cpp create mode 100644 src/Editor/Undo/CResizeScriptArrayCommand.h diff --git a/src/Core/Resource/Script/IProperty.cpp b/src/Core/Resource/Script/IProperty.cpp index ec335575..83ac6a4f 100644 --- a/src/Core/Resource/Script/IProperty.cpp +++ b/src/Core/Resource/Script/IProperty.cpp @@ -23,6 +23,19 @@ TIDString IProperty::IDString(bool FullPath) const } // ************ CPropertyStruct ************ +void CPropertyStruct::Copy(const IProperty *pkProp) +{ + const CPropertyStruct *pkSource = static_cast(pkProp); + + for (auto it = mProperties.begin(); it != mProperties.end(); it++) + delete *it; + + mProperties.resize(pkSource->mProperties.size()); + + for (u32 iSub = 0; iSub < mProperties.size(); iSub++) + mProperties[iSub] = pkSource->mProperties[iSub]->Clone(this); +} + IProperty* CPropertyStruct::PropertyByIndex(u32 index) const { return mProperties[index]; diff --git a/src/Core/Resource/Script/IProperty.h b/src/Core/Resource/Script/IProperty.h index ad97f9a2..18e1433e 100644 --- a/src/Core/Resource/Script/IProperty.h +++ b/src/Core/Resource/Script/IProperty.h @@ -41,8 +41,11 @@ public: virtual EPropertyType Type() const = 0; virtual TString ToString() const { return ""; } virtual IPropertyValue* RawValue() { return nullptr; } + virtual void Copy(const IProperty *pkProp) = 0; + virtual IProperty* Clone(CPropertyStruct *pParent = 0) const = 0; inline CPropertyStruct* Parent() const { return mpParent; } + inline void SetParent(CPropertyStruct *pParent) { mpParent = pParent; } // These functions can't be in the header to avoid circular includes with IPropertyTemplate.h IPropertyTemplate* Template() const; @@ -71,6 +74,21 @@ public: virtual TString ToString() const { return mValue.ToString(); } virtual IPropertyValue* RawValue() { return &mValue; } + virtual void Copy(const IProperty *pkProp) + { + const TTypedProperty *pkCast = static_cast(pkProp); + mValue.Set(pkCast->mValue.Get()); + } + + virtual TTypedProperty* Clone(CPropertyStruct *pParent) const + { + if (!pParent) pParent = mpParent; + + TTypedProperty *pOut = new TTypedProperty(mpTemplate, pParent); + pOut->Copy(this); + return pOut; + } + inline PropType Get() const { return mValue.Get(); } inline void Set(PropType v) { mValue.Set(v); } }; @@ -107,6 +125,16 @@ public: EPropertyType Type() const { return eStructProperty; } + virtual void Copy(const IProperty *pkProp); + + virtual IProperty* Clone(CPropertyStruct *pParent) const + { + if (!pParent) pParent = mpParent; + CPropertyStruct *pOut = new CPropertyStruct(mpTemplate, pParent); + pOut->Copy(this); + return pOut; + } + // Inline inline u32 Count() const { return mProperties.size(); } inline void AddSubProperty(IProperty *pProp) { mProperties.push_back(pProp); } @@ -134,6 +162,14 @@ public: EPropertyType Type() const { return eArrayProperty; } + virtual IProperty* Clone(CPropertyStruct *pParent) const + { + if (!pParent) pParent = mpParent; + CArrayProperty *pOut = new CArrayProperty(mpTemplate, pParent); + pOut->Copy(this); + return pOut; + } + // Inline inline void Reserve(u32 amount) { mProperties.reserve(amount); } diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index 80d049ea..2652e005 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -134,7 +134,9 @@ HEADERS += \ PropertyEdit/CPropertyRelay.h \ WorldEditor/CInstancesProxyModel.h \ WorldEditor/CInstancesModel.h \ - Undo/CEditScriptPropertyCommand.h + Undo/CEditScriptPropertyCommand.h \ + Undo/CResizeScriptArrayCommand.h \ + Undo/CBasicPropertyCommand.h # Source Files SOURCES += \ @@ -186,7 +188,9 @@ SOURCES += \ PropertyEdit/CPropertyDelegate.cpp \ PropertyEdit/CPropertyView.cpp \ WorldEditor/CInstancesModel.cpp \ - Undo/CEditScriptPropertyCommand.cpp + Undo/CEditScriptPropertyCommand.cpp \ + Undo/CResizeScriptArrayCommand.cpp \ + Undo/CBasicPropertyCommand.cpp # UI Files FORMS += \ diff --git a/src/Editor/PropertyEdit/CPropertyDelegate.cpp b/src/Editor/PropertyEdit/CPropertyDelegate.cpp index 6a7f0d16..44925167 100644 --- a/src/Editor/PropertyEdit/CPropertyDelegate.cpp +++ b/src/Editor/PropertyEdit/CPropertyDelegate.cpp @@ -3,6 +3,7 @@ #include "Editor/UICommon.h" #include "Editor/Undo/CEditScriptPropertyCommand.h" +#include "Editor/Undo/CResizeScriptArrayCommand.h" #include "Editor/Widgets/WColorPicker.h" #include "Editor/Widgets/WDraggableSpinBox.h" #include "Editor/Widgets/WIntegralSpinBox.h" @@ -351,7 +352,8 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo if (pProp) { - pOldValue = pProp->RawValue()->Clone(); + IPropertyValue *pRawValue = pProp->RawValue(); + pOldValue = pRawValue ? pRawValue->Clone() : nullptr; switch (pProp->Type()) { @@ -430,8 +432,14 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo case eArrayProperty: { WIntegralSpinBox *pSpinBox = static_cast(pEditor); + CArrayProperty *pArray = static_cast(pProp); u32 NewCount = pSpinBox->value(); - mpModel->ResizeArray(rkIndex, NewCount); + + if (pArray->Count() != NewCount) + { + CResizeScriptArrayCommand *pCmd = new CResizeScriptArrayCommand(mpModel, rkIndex, NewCount); + mpEditor->UndoStack()->push(pCmd); + } break; } @@ -442,7 +450,9 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo else if (rkIndex.internalId() & 0x1) { pProp = mpModel->PropertyForIndex(rkIndex, true); - pOldValue = pProp->RawValue()->Clone(); + + IPropertyValue *pOldValue = pProp->RawValue(); + pOldValue = pOldValue ? pOldValue->Clone() : nullptr; if (pProp->Type() == eCharacterProperty) SetCharacterModelData(pEditor, rkIndex); @@ -490,7 +500,7 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo } } - if (pProp) + if (pProp && pOldValue) { // Check for edit in progress bool Matches = pOldValue->Matches(pProp->RawValue()); diff --git a/src/Editor/PropertyEdit/CPropertyModel.cpp b/src/Editor/PropertyEdit/CPropertyModel.cpp index 9af5b3c7..e69dc610 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.cpp +++ b/src/Editor/PropertyEdit/CPropertyModel.cpp @@ -409,29 +409,35 @@ void CPropertyModel::NotifyPropertyModified(const QModelIndex& rkIndex) emit PropertyModified(rkIndex); } -void CPropertyModel::ResizeArray(const QModelIndex& rkIndex, u32 NewSize) +void CPropertyModel::ArrayAboutToBeResized(const QModelIndex& rkIndex, u32 NewSize) { - QModelIndex Index = index(rkIndex.row(), 0, rkIndex.parent()); - CArrayProperty *pArray = static_cast(PropertyForIndex(rkIndex, false)); + QModelIndex Index = rkIndex.sibling(rkIndex.row(), 0); + CArrayProperty *pArray = static_cast(PropertyForIndex(Index, false)); if (pArray && pArray->Type() == eArrayProperty) { u32 OldSize = pArray->Count(); - if (OldSize != NewSize) + if (NewSize != OldSize) { - if (OldSize < NewSize) - { + if (NewSize > OldSize) beginInsertRows(Index, OldSize, NewSize - 1); - pArray->Resize(NewSize); - endInsertRows(); - } else - { beginRemoveRows(Index, NewSize, OldSize - 1); - pArray->Resize(NewSize); - endRemoveRows(); - } } } } + +void CPropertyModel::ArrayResized(const QModelIndex& rkIndex, u32 OldSize) +{ + CArrayProperty *pArray = static_cast(PropertyForIndex(rkIndex, false)); + u32 NewSize = pArray->Count(); + + if (NewSize != OldSize) + { + if (pArray->Count() > OldSize) + endInsertRows(); + else + endRemoveRows(); + } +} diff --git a/src/Editor/PropertyEdit/CPropertyModel.h b/src/Editor/PropertyEdit/CPropertyModel.h index e672511d..f42d9b32 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.h +++ b/src/Editor/PropertyEdit/CPropertyModel.h @@ -24,8 +24,9 @@ public: Qt::ItemFlags flags(const QModelIndex& rkIndex) const; void NotifyPropertyModified(const QModelIndex& rkIndex); + void ArrayAboutToBeResized(const QModelIndex& rkIndex, u32 NewSize); + void ArrayResized(const QModelIndex& rkIndex, u32 OldSize); void ResizeArray(const QModelIndex& rkIndex, u32 NewSize); - signals: void PropertyModified(const QModelIndex& rkIndex); }; diff --git a/src/Editor/Undo/CBasicPropertyCommand.cpp b/src/Editor/Undo/CBasicPropertyCommand.cpp new file mode 100644 index 00000000..0b964b54 --- /dev/null +++ b/src/Editor/Undo/CBasicPropertyCommand.cpp @@ -0,0 +1,87 @@ +#include "CBasicPropertyCommand.h" +#include + +CBasicPropertyCommand::CBasicPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex) + : QUndoCommand("Edit Property") + , mpModel(pModel) + , mpProperty(pModel->PropertyForIndex(rkIndex, true)) + , mpTemplate(mpProperty->Template()) + , mIndex(rkIndex) + , mIsInArray(false) +{ + // Check for array + IProperty *pProp = mpProperty; + IProperty *pParent = mpProperty->Parent(); + + while (pParent) + { + if (pParent->Type() == eArrayProperty) + { + mIsInArray = true; + + // Find array index + CArrayProperty *pArray = static_cast(pParent); + + for (u32 iSub = 0; iSub < pArray->Count(); iSub++) + { + if (pArray->PropertyByIndex(iSub) == pProp) + { + mArrayIndices << iSub; + break; + } + } + } + pProp = pParent; + pParent = pParent->Parent(); + } +} + +void CBasicPropertyCommand::UpdateArraySubProperty() +{ + // If an array has been sized down and then back up, then we might have an index to an invalid property. + // Since we can't assume our index is still valid, we'll use the template and the model to find the corresponding property. + IPropertyTemplate *pTemp = mpTemplate; + CStructTemplate *pParent = mpTemplate->Parent(); + + QVector SubIndices; + int IndexIndex = 0; + + if (mIndex.internalId() & 0x1) + SubIndices << mIndex.row(); + + while (pParent) + { + if (pParent->Type() != eArrayProperty || static_cast(pParent)->Count() > 1) + { + for (u32 iSub = 0; iSub < pParent->Count(); iSub++) + { + if (pParent->PropertyByIndex(iSub) == pTemp) + { + SubIndices << iSub; + break; + } + } + } + + if (pParent->Type() == eArrayProperty) + { + SubIndices << mArrayIndices[IndexIndex]; + IndexIndex++; + } + + pTemp = pParent; + pParent = pParent->Parent(); + } + + // Find corresponding index + QModelIndex Index = QModelIndex(); + + for (int iSub = SubIndices.size() - 1; iSub >= 0; iSub--) + Index = mpModel->index(SubIndices[iSub], 0, Index); + + Index = Index.sibling(Index.row(), 1); + + // Get property + mpProperty = mpModel->PropertyForIndex(Index, true); + mIndex = Index; +} diff --git a/src/Editor/Undo/CBasicPropertyCommand.h b/src/Editor/Undo/CBasicPropertyCommand.h new file mode 100644 index 00000000..738e93d9 --- /dev/null +++ b/src/Editor/Undo/CBasicPropertyCommand.h @@ -0,0 +1,23 @@ +#ifndef CBASICPROPERTYCOMMAND_H +#define CBASICPROPERTYCOMMAND_H + +#include "Editor/PropertyEdit/CPropertyModel.h" +#include + +class CBasicPropertyCommand : public QUndoCommand +{ +protected: + CPropertyModel *mpModel; + IProperty *mpProperty; + IPropertyTemplate *mpTemplate; + QModelIndex mIndex; + + bool mIsInArray; + QVector mArrayIndices; + +public: + CBasicPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex); + virtual void UpdateArraySubProperty(); +}; + +#endif // CBASICPROPERTYCOMMAND_H diff --git a/src/Editor/Undo/CEditScriptPropertyCommand.cpp b/src/Editor/Undo/CEditScriptPropertyCommand.cpp index 24624351..80915398 100644 --- a/src/Editor/Undo/CEditScriptPropertyCommand.cpp +++ b/src/Editor/Undo/CEditScriptPropertyCommand.cpp @@ -2,12 +2,9 @@ #include "EUndoCommand.h" CEditScriptPropertyCommand::CEditScriptPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex, IPropertyValue *pOldValue, bool IsDone) - : QUndoCommand("Edit Property") - , mpModel(pModel) - , mIndex(rkIndex) + : CBasicPropertyCommand(pModel, rkIndex) , mCommandEnded(IsDone) { - mpProperty = pModel->PropertyForIndex(rkIndex, true); mpOldValue = pOldValue; mpNewValue = mpProperty->RawValue()->Clone(); } @@ -42,12 +39,14 @@ bool CEditScriptPropertyCommand::mergeWith(const QUndoCommand *pkOther) void CEditScriptPropertyCommand::undo() { + if (mIsInArray) UpdateArraySubProperty(); mpProperty->RawValue()->Copy(mpOldValue); mpModel->NotifyPropertyModified(mIndex); } void CEditScriptPropertyCommand::redo() { + if (mIsInArray) UpdateArraySubProperty(); mpProperty->RawValue()->Copy(mpNewValue); mpModel->NotifyPropertyModified(mIndex); } diff --git a/src/Editor/Undo/CEditScriptPropertyCommand.h b/src/Editor/Undo/CEditScriptPropertyCommand.h index cbc94542..374610d4 100644 --- a/src/Editor/Undo/CEditScriptPropertyCommand.h +++ b/src/Editor/Undo/CEditScriptPropertyCommand.h @@ -1,15 +1,12 @@ #ifndef CEDITSCRIPTPROPERTYCOMMAND_H #define CEDITSCRIPTPROPERTYCOMMAND_H -#include "Editor/PropertyEdit/CPropertyDelegate.h" +#include "CBasicPropertyCommand.h" +#include "Editor/PropertyEdit/CPropertyModel.h" #include -class CEditScriptPropertyCommand : public QUndoCommand +class CEditScriptPropertyCommand : public CBasicPropertyCommand { - CPropertyModel *mpModel; - IProperty *mpProperty; - QModelIndex mIndex; - IPropertyValue *mpOldValue; IPropertyValue *mpNewValue; bool mCommandEnded; diff --git a/src/Editor/Undo/CResizeScriptArrayCommand.cpp b/src/Editor/Undo/CResizeScriptArrayCommand.cpp new file mode 100644 index 00000000..0812956d --- /dev/null +++ b/src/Editor/Undo/CResizeScriptArrayCommand.cpp @@ -0,0 +1,74 @@ +#include "CResizeScriptArrayCommand.h" + +CResizeScriptArrayCommand::CResizeScriptArrayCommand(CPropertyModel *pModel, const QModelIndex& rkIndex, u32 NewSize) + : CBasicPropertyCommand(pModel, rkIndex) + , mpArray(static_cast(mpProperty)) + , mOldSize(mpArray->Count()) + , mNewSize(NewSize) +{ + mNewSizeLarger = mNewSize > mOldSize; + + if (!mNewSizeLarger) + { + for (u32 iSub = mNewSize; iSub < mOldSize; iSub++) + { + mDeletedProperties << mpArray->PropertyByIndex(iSub)->Clone(); + } + } +} + +CResizeScriptArrayCommand::~CResizeScriptArrayCommand() +{ + foreach (IProperty *pProp, mDeletedProperties) + delete pProp; +} + +void CResizeScriptArrayCommand::undo() +{ + if (mNewSize != mOldSize) + { + if (mIsInArray) UpdateArraySubProperty(); + + mpModel->ArrayAboutToBeResized(mIndex, mOldSize); + mpArray->Resize(mOldSize); + + if (!mNewSizeLarger) + { + u32 NumNewElements = mOldSize - mNewSize; + + for (u32 iSub = 0; iSub < NumNewElements; iSub++) + { + u32 Idx = iSub + mNewSize; + mpArray->PropertyByIndex(Idx)->Copy(mDeletedProperties[iSub]); + } + } + + mpModel->ArrayResized(mIndex, mNewSize); + } +} + +void CResizeScriptArrayCommand::redo() +{ + // Whether we're increasing or decreasing in size, there's no need to restore deleted properties on redo. + if (mNewSize != mOldSize) + { + if (mIsInArray) UpdateArraySubProperty(); + + mpModel->ArrayAboutToBeResized(mIndex, mNewSize); + mpArray->Resize(mNewSize); + mpModel->ArrayResized(mIndex, mOldSize); + } +} + +void CResizeScriptArrayCommand::UpdateArraySubProperty() +{ + CArrayProperty *pOldArray = mpArray; + CBasicPropertyCommand::UpdateArraySubProperty(); + mpArray = static_cast(mpProperty); + + if (pOldArray != mpArray) + { + for (int iDel = 0; iDel < mDeletedProperties.size(); iDel++) + mDeletedProperties[iDel]->SetParent(mpArray); + } +} diff --git a/src/Editor/Undo/CResizeScriptArrayCommand.h b/src/Editor/Undo/CResizeScriptArrayCommand.h new file mode 100644 index 00000000..082c24e5 --- /dev/null +++ b/src/Editor/Undo/CResizeScriptArrayCommand.h @@ -0,0 +1,25 @@ +#ifndef CRESIZESCRIPTARRAYCOMMAND_H +#define CRESIZESCRIPTARRAYCOMMAND_H + +#include "CBasicPropertyCommand.h" +#include "Editor/PropertyEdit/CPropertyModel.h" +#include + +class CResizeScriptArrayCommand : public CBasicPropertyCommand +{ + CArrayProperty *mpArray; + QVector mDeletedProperties; + + u32 mOldSize; + u32 mNewSize; + bool mNewSizeLarger; + +public: + CResizeScriptArrayCommand(CPropertyModel *pModel, const QModelIndex& rkIndex, u32 NewSize); + ~CResizeScriptArrayCommand(); + void undo(); + void redo(); + virtual void UpdateArraySubProperty(); +}; + +#endif // CRESIZESCRIPTARRAYCOMMAND_H diff --git a/src/Editor/Undo/UndoCommands.h b/src/Editor/Undo/UndoCommands.h index 26958150..0f983796 100644 --- a/src/Editor/Undo/UndoCommands.h +++ b/src/Editor/Undo/UndoCommands.h @@ -10,6 +10,7 @@ #include "CSelectAllCommand.h" #include "CInvertSelectionCommand.h" #include "CEditScriptPropertyCommand.h" +#include "CResizeScriptArrayCommand.h" #include "EUndoCommand.h" #endif // UNDOCOMMANDS