Support for undo/redo on array resize

This commit is contained in:
parax0 2016-01-31 14:25:16 -07:00
parent cc054cf571
commit f6f36f4f2e
13 changed files with 306 additions and 30 deletions

View File

@ -23,6 +23,19 @@ TIDString IProperty::IDString(bool FullPath) const
} }
// ************ CPropertyStruct ************ // ************ CPropertyStruct ************
void CPropertyStruct::Copy(const IProperty *pkProp)
{
const CPropertyStruct *pkSource = static_cast<const CPropertyStruct*>(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 IProperty* CPropertyStruct::PropertyByIndex(u32 index) const
{ {
return mProperties[index]; return mProperties[index];

View File

@ -41,8 +41,11 @@ public:
virtual EPropertyType Type() const = 0; virtual EPropertyType Type() const = 0;
virtual TString ToString() const { return ""; } virtual TString ToString() const { return ""; }
virtual IPropertyValue* RawValue() { return nullptr; } 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 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 // These functions can't be in the header to avoid circular includes with IPropertyTemplate.h
IPropertyTemplate* Template() const; IPropertyTemplate* Template() const;
@ -71,6 +74,21 @@ public:
virtual TString ToString() const { return mValue.ToString(); } virtual TString ToString() const { return mValue.ToString(); }
virtual IPropertyValue* RawValue() { return &mValue; } virtual IPropertyValue* RawValue() { return &mValue; }
virtual void Copy(const IProperty *pkProp)
{
const TTypedProperty *pkCast = static_cast<const TTypedProperty*>(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 PropType Get() const { return mValue.Get(); }
inline void Set(PropType v) { mValue.Set(v); } inline void Set(PropType v) { mValue.Set(v); }
}; };
@ -107,6 +125,16 @@ public:
EPropertyType Type() const { return eStructProperty; } 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
inline u32 Count() const { return mProperties.size(); } inline u32 Count() const { return mProperties.size(); }
inline void AddSubProperty(IProperty *pProp) { mProperties.push_back(pProp); } inline void AddSubProperty(IProperty *pProp) { mProperties.push_back(pProp); }
@ -134,6 +162,14 @@ public:
EPropertyType Type() const { return eArrayProperty; } 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
inline void Reserve(u32 amount) { mProperties.reserve(amount); } inline void Reserve(u32 amount) { mProperties.reserve(amount); }

View File

@ -134,7 +134,9 @@ HEADERS += \
PropertyEdit/CPropertyRelay.h \ PropertyEdit/CPropertyRelay.h \
WorldEditor/CInstancesProxyModel.h \ WorldEditor/CInstancesProxyModel.h \
WorldEditor/CInstancesModel.h \ WorldEditor/CInstancesModel.h \
Undo/CEditScriptPropertyCommand.h Undo/CEditScriptPropertyCommand.h \
Undo/CResizeScriptArrayCommand.h \
Undo/CBasicPropertyCommand.h
# Source Files # Source Files
SOURCES += \ SOURCES += \
@ -186,7 +188,9 @@ SOURCES += \
PropertyEdit/CPropertyDelegate.cpp \ PropertyEdit/CPropertyDelegate.cpp \
PropertyEdit/CPropertyView.cpp \ PropertyEdit/CPropertyView.cpp \
WorldEditor/CInstancesModel.cpp \ WorldEditor/CInstancesModel.cpp \
Undo/CEditScriptPropertyCommand.cpp Undo/CEditScriptPropertyCommand.cpp \
Undo/CResizeScriptArrayCommand.cpp \
Undo/CBasicPropertyCommand.cpp
# UI Files # UI Files
FORMS += \ FORMS += \

View File

@ -3,6 +3,7 @@
#include "Editor/UICommon.h" #include "Editor/UICommon.h"
#include "Editor/Undo/CEditScriptPropertyCommand.h" #include "Editor/Undo/CEditScriptPropertyCommand.h"
#include "Editor/Undo/CResizeScriptArrayCommand.h"
#include "Editor/Widgets/WColorPicker.h" #include "Editor/Widgets/WColorPicker.h"
#include "Editor/Widgets/WDraggableSpinBox.h" #include "Editor/Widgets/WDraggableSpinBox.h"
#include "Editor/Widgets/WIntegralSpinBox.h" #include "Editor/Widgets/WIntegralSpinBox.h"
@ -351,7 +352,8 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo
if (pProp) if (pProp)
{ {
pOldValue = pProp->RawValue()->Clone(); IPropertyValue *pRawValue = pProp->RawValue();
pOldValue = pRawValue ? pRawValue->Clone() : nullptr;
switch (pProp->Type()) switch (pProp->Type())
{ {
@ -430,8 +432,14 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo
case eArrayProperty: case eArrayProperty:
{ {
WIntegralSpinBox *pSpinBox = static_cast<WIntegralSpinBox*>(pEditor); WIntegralSpinBox *pSpinBox = static_cast<WIntegralSpinBox*>(pEditor);
CArrayProperty *pArray = static_cast<CArrayProperty*>(pProp);
u32 NewCount = pSpinBox->value(); u32 NewCount = pSpinBox->value();
mpModel->ResizeArray(rkIndex, NewCount);
if (pArray->Count() != NewCount)
{
CResizeScriptArrayCommand *pCmd = new CResizeScriptArrayCommand(mpModel, rkIndex, NewCount);
mpEditor->UndoStack()->push(pCmd);
}
break; break;
} }
@ -442,7 +450,9 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo
else if (rkIndex.internalId() & 0x1) else if (rkIndex.internalId() & 0x1)
{ {
pProp = mpModel->PropertyForIndex(rkIndex, true); pProp = mpModel->PropertyForIndex(rkIndex, true);
pOldValue = pProp->RawValue()->Clone();
IPropertyValue *pOldValue = pProp->RawValue();
pOldValue = pOldValue ? pOldValue->Clone() : nullptr;
if (pProp->Type() == eCharacterProperty) if (pProp->Type() == eCharacterProperty)
SetCharacterModelData(pEditor, rkIndex); SetCharacterModelData(pEditor, rkIndex);
@ -490,7 +500,7 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo
} }
} }
if (pProp) if (pProp && pOldValue)
{ {
// Check for edit in progress // Check for edit in progress
bool Matches = pOldValue->Matches(pProp->RawValue()); bool Matches = pOldValue->Matches(pProp->RawValue());

View File

@ -409,29 +409,35 @@ void CPropertyModel::NotifyPropertyModified(const QModelIndex& rkIndex)
emit PropertyModified(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()); QModelIndex Index = rkIndex.sibling(rkIndex.row(), 0);
CArrayProperty *pArray = static_cast<CArrayProperty*>(PropertyForIndex(rkIndex, false)); CArrayProperty *pArray = static_cast<CArrayProperty*>(PropertyForIndex(Index, false));
if (pArray && pArray->Type() == eArrayProperty) if (pArray && pArray->Type() == eArrayProperty)
{ {
u32 OldSize = pArray->Count(); u32 OldSize = pArray->Count();
if (OldSize != NewSize) if (NewSize != OldSize)
{ {
if (OldSize < NewSize) if (NewSize > OldSize)
{
beginInsertRows(Index, OldSize, NewSize - 1); beginInsertRows(Index, OldSize, NewSize - 1);
pArray->Resize(NewSize);
endInsertRows();
}
else else
{
beginRemoveRows(Index, NewSize, OldSize - 1); beginRemoveRows(Index, NewSize, OldSize - 1);
pArray->Resize(NewSize);
endRemoveRows();
}
} }
} }
} }
void CPropertyModel::ArrayResized(const QModelIndex& rkIndex, u32 OldSize)
{
CArrayProperty *pArray = static_cast<CArrayProperty*>(PropertyForIndex(rkIndex, false));
u32 NewSize = pArray->Count();
if (NewSize != OldSize)
{
if (pArray->Count() > OldSize)
endInsertRows();
else
endRemoveRows();
}
}

View File

@ -24,8 +24,9 @@ public:
Qt::ItemFlags flags(const QModelIndex& rkIndex) const; Qt::ItemFlags flags(const QModelIndex& rkIndex) const;
void NotifyPropertyModified(const QModelIndex& rkIndex); 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); void ResizeArray(const QModelIndex& rkIndex, u32 NewSize);
signals: signals:
void PropertyModified(const QModelIndex& rkIndex); void PropertyModified(const QModelIndex& rkIndex);
}; };

View File

@ -0,0 +1,87 @@
#include "CBasicPropertyCommand.h"
#include <Core/Resource/Script/IPropertyTemplate.h>
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<CArrayProperty*>(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<u32> SubIndices;
int IndexIndex = 0;
if (mIndex.internalId() & 0x1)
SubIndices << mIndex.row();
while (pParent)
{
if (pParent->Type() != eArrayProperty || static_cast<CArrayTemplate*>(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;
}

View File

@ -0,0 +1,23 @@
#ifndef CBASICPROPERTYCOMMAND_H
#define CBASICPROPERTYCOMMAND_H
#include "Editor/PropertyEdit/CPropertyModel.h"
#include <QUndoCommand>
class CBasicPropertyCommand : public QUndoCommand
{
protected:
CPropertyModel *mpModel;
IProperty *mpProperty;
IPropertyTemplate *mpTemplate;
QModelIndex mIndex;
bool mIsInArray;
QVector<u32> mArrayIndices;
public:
CBasicPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex);
virtual void UpdateArraySubProperty();
};
#endif // CBASICPROPERTYCOMMAND_H

View File

@ -2,12 +2,9 @@
#include "EUndoCommand.h" #include "EUndoCommand.h"
CEditScriptPropertyCommand::CEditScriptPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex, IPropertyValue *pOldValue, bool IsDone) CEditScriptPropertyCommand::CEditScriptPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex, IPropertyValue *pOldValue, bool IsDone)
: QUndoCommand("Edit Property") : CBasicPropertyCommand(pModel, rkIndex)
, mpModel(pModel)
, mIndex(rkIndex)
, mCommandEnded(IsDone) , mCommandEnded(IsDone)
{ {
mpProperty = pModel->PropertyForIndex(rkIndex, true);
mpOldValue = pOldValue; mpOldValue = pOldValue;
mpNewValue = mpProperty->RawValue()->Clone(); mpNewValue = mpProperty->RawValue()->Clone();
} }
@ -42,12 +39,14 @@ bool CEditScriptPropertyCommand::mergeWith(const QUndoCommand *pkOther)
void CEditScriptPropertyCommand::undo() void CEditScriptPropertyCommand::undo()
{ {
if (mIsInArray) UpdateArraySubProperty();
mpProperty->RawValue()->Copy(mpOldValue); mpProperty->RawValue()->Copy(mpOldValue);
mpModel->NotifyPropertyModified(mIndex); mpModel->NotifyPropertyModified(mIndex);
} }
void CEditScriptPropertyCommand::redo() void CEditScriptPropertyCommand::redo()
{ {
if (mIsInArray) UpdateArraySubProperty();
mpProperty->RawValue()->Copy(mpNewValue); mpProperty->RawValue()->Copy(mpNewValue);
mpModel->NotifyPropertyModified(mIndex); mpModel->NotifyPropertyModified(mIndex);
} }

View File

@ -1,15 +1,12 @@
#ifndef CEDITSCRIPTPROPERTYCOMMAND_H #ifndef CEDITSCRIPTPROPERTYCOMMAND_H
#define CEDITSCRIPTPROPERTYCOMMAND_H #define CEDITSCRIPTPROPERTYCOMMAND_H
#include "Editor/PropertyEdit/CPropertyDelegate.h" #include "CBasicPropertyCommand.h"
#include "Editor/PropertyEdit/CPropertyModel.h"
#include <QUndoCommand> #include <QUndoCommand>
class CEditScriptPropertyCommand : public QUndoCommand class CEditScriptPropertyCommand : public CBasicPropertyCommand
{ {
CPropertyModel *mpModel;
IProperty *mpProperty;
QModelIndex mIndex;
IPropertyValue *mpOldValue; IPropertyValue *mpOldValue;
IPropertyValue *mpNewValue; IPropertyValue *mpNewValue;
bool mCommandEnded; bool mCommandEnded;

View File

@ -0,0 +1,74 @@
#include "CResizeScriptArrayCommand.h"
CResizeScriptArrayCommand::CResizeScriptArrayCommand(CPropertyModel *pModel, const QModelIndex& rkIndex, u32 NewSize)
: CBasicPropertyCommand(pModel, rkIndex)
, mpArray(static_cast<CArrayProperty*>(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<CArrayProperty*>(mpProperty);
if (pOldArray != mpArray)
{
for (int iDel = 0; iDel < mDeletedProperties.size(); iDel++)
mDeletedProperties[iDel]->SetParent(mpArray);
}
}

View File

@ -0,0 +1,25 @@
#ifndef CRESIZESCRIPTARRAYCOMMAND_H
#define CRESIZESCRIPTARRAYCOMMAND_H
#include "CBasicPropertyCommand.h"
#include "Editor/PropertyEdit/CPropertyModel.h"
#include <QUndoCommand>
class CResizeScriptArrayCommand : public CBasicPropertyCommand
{
CArrayProperty *mpArray;
QVector<IProperty*> 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

View File

@ -10,6 +10,7 @@
#include "CSelectAllCommand.h" #include "CSelectAllCommand.h"
#include "CInvertSelectionCommand.h" #include "CInvertSelectionCommand.h"
#include "CEditScriptPropertyCommand.h" #include "CEditScriptPropertyCommand.h"
#include "CResizeScriptArrayCommand.h"
#include "EUndoCommand.h" #include "EUndoCommand.h"
#endif // UNDOCOMMANDS #endif // UNDOCOMMANDS