Support for undoing property changes + tons of tweaks and fixes

This commit is contained in:
parax0 2016-01-31 01:11:32 -07:00
parent c7d448225c
commit cc054cf571
25 changed files with 467 additions and 192 deletions

View File

@ -33,6 +33,18 @@ public:
void SetResource(CResource *pRes);
void SetNodeIndex(u32 Index);
void SetUnknown(u32 Index, u32 Value);
// Operators
inline bool operator==(const CAnimationParameters& rkOther) const
{
return ( (mGame == rkOther.mGame) &&
(mpCharSet == rkOther.mpCharSet) &&
(mNodeIndex == rkOther.mNodeIndex) &&
(mUnknown1 == rkOther.mUnknown1) &&
(mUnknown2 == rkOther.mUnknown2) &&
(mUnknown3 == rkOther.mUnknown3) &&
(mUnknown4 == rkOther.mUnknown4) );
}
};
#endif // CANIMATIONPARAMETERS_H

View File

@ -196,6 +196,7 @@ CResource* CResCache::GetResource(const TString& ResPath)
else if (type == "CHAR") Res = CAnimSetLoader::LoadCHAR(file);
else if (type == "MREA") Res = CAreaLoader::LoadMREA(file);
else if (type == "MLVL") Res = CWorldLoader::LoadMLVL(file);
else if (type == "STRG") Res = CStringLoader::LoadSTRG(file);
else if (type == "FONT") Res = CFontLoader::LoadFONT(file);
else if (type == "SCAN") Res = CScanLoader::LoadSCAN(file);
else if (type == "DCLN") Res = CCollisionLoader::LoadDCLN(file);

View File

@ -31,11 +31,19 @@ public:
inline CUniqueID ID() const
{
TString FileName = mPath.GetFileName();
if (!mIsPath)
return CUniqueID::FromString(mPath.GetFileName());
return CUniqueID::FromString(FileName);
else
{
if (FileName.IsHexString() && (FileName.Size() == 8 || FileName.Size() == 16))
return CUniqueID::FromString(FileName);
else
return CUniqueID::skInvalidID64;
}
}
inline CFourCC Type() const
{
@ -64,6 +72,16 @@ public:
else
return mIsValidPath;
}
inline bool operator==(const CResourceInfo& rkOther) const
{
return (mPath.GetFileName() == rkOther.mPath.GetFileName());
}
inline bool operator!=(const CResourceInfo& rkOther) const
{
return (!(*this == rkOther));
}
};
#endif // CRESOURCEINFO

View File

@ -99,7 +99,7 @@ void CScriptLoader::ReadProperty(IProperty *pProp, u32 Size, IInputStream& SCLY)
CUniqueID ResID = (mVersion < eCorruptionProto ? SCLY.ReadLong() : SCLY.ReadLongLong());
const TStringList& rkExtensions = static_cast<CFileTemplate*>(pTemp)->Extensions();
CResourceInfo Info(ResID, "UNKN");
CResourceInfo Info(ResID, CFourCC(!rkExtensions.empty() ? rkExtensions.front() : "UNKN"));
if (ResID.IsValid())
{

View File

@ -40,6 +40,7 @@ public:
virtual ~IProperty() {}
virtual EPropertyType Type() const = 0;
virtual TString ToString() const { return ""; }
virtual IPropertyValue* RawValue() { return nullptr; }
inline CPropertyStruct* Parent() const { return mpParent; }
@ -68,6 +69,7 @@ public:
~TTypedProperty() {}
virtual EPropertyType Type() const { return TypeEnum; }
virtual TString ToString() const { return mValue.ToString(); }
virtual IPropertyValue* RawValue() { return &mValue; }
inline PropType Get() const { return mValue.Get(); }
inline void Set(PropType v) { mValue.Set(v); }
@ -77,7 +79,7 @@ typedef TTypedProperty<char, eByteProperty, CByteValue>
typedef TTypedProperty<short, eShortProperty, CShortValue> TShortProperty;
typedef TTypedProperty<long, eLongProperty, CLongValue> TLongProperty;
typedef TTypedProperty<long, eEnumProperty, CLongValue> TEnumProperty;
typedef TTypedProperty<long, eBitfieldProperty, CLongValue> TBitfieldProperty;
typedef TTypedProperty<long, eBitfieldProperty, CHexLongValue> TBitfieldProperty;
typedef TTypedProperty<float, eFloatProperty, CFloatValue> TFloatProperty;
typedef TTypedProperty<TString, eStringProperty, CStringValue> TStringProperty;
typedef TTypedProperty<CVector3f, eVector3Property, CVector3Value> TVector3Property;

View File

@ -338,13 +338,11 @@ public:
CEnumTemplate(u32 ID, CStructTemplate *pParent = 0)
: TLongTemplate(ID, pParent)
{
mDefaultValue.SetHexStringOutput(true);
}
CEnumTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CStructTemplate *pParent = 0)
: TLongTemplate(ID, rkName, CookPreference, pParent)
{
mDefaultValue.SetHexStringOutput(true);
}
virtual EPropertyType Type() const { return eEnumProperty; }
@ -393,7 +391,7 @@ public:
// CBitfieldTemplate - Property template for bitfields, which can have multiple
// distinct boolean parameters packed into one property.
class CBitfieldTemplate : public TLongTemplate
class CBitfieldTemplate : public TTypedPropertyTemplate<u32, eBitfieldProperty, CHexLongValue>
{
friend class CTemplateLoader;
friend class CTemplateWriter;
@ -411,15 +409,13 @@ class CBitfieldTemplate : public TLongTemplate
public:
CBitfieldTemplate(u32 ID, CStructTemplate *pParent = 0)
: TLongTemplate(ID, pParent)
: TTypedPropertyTemplate(ID, pParent)
{
mDefaultValue.SetHexStringOutput(true);
}
CBitfieldTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CStructTemplate *pParent = 0)
: TLongTemplate(ID, rkName, CookPreference, pParent)
: TTypedPropertyTemplate(ID, rkName, CookPreference, pParent)
{
mDefaultValue.SetHexStringOutput(true);
}
virtual EPropertyType Type() const { return eBitfieldProperty; }

View File

@ -17,6 +17,9 @@ class IPropertyValue
public:
virtual TString ToString() const = 0;
virtual void FromString(const TString& rkString) = 0;
virtual IPropertyValue* Clone() const = 0;
virtual void Copy(IPropertyValue *pValue) = 0;
virtual bool Matches(IPropertyValue *pValue) const = 0;
};
template<typename PropType>
@ -28,8 +31,20 @@ protected:
public:
TTypedPropertyValue() {}
TTypedPropertyValue(PropType Val)
: mValue(Val) {}
TTypedPropertyValue(PropType rkVal)
: mValue(rkVal) {}
virtual void Copy(IPropertyValue *pValue)
{
TTypedPropertyValue *pOther = static_cast<TTypedPropertyValue*>(pValue);
mValue = pOther->mValue;
}
virtual bool Matches(IPropertyValue *pValue) const
{
TTypedPropertyValue *pOther = static_cast<TTypedPropertyValue*>(pValue);
return (mValue == pOther->mValue);
}
PropType Get() const
{
@ -69,6 +84,11 @@ public:
{
mValue = (rkString == "true");
}
IPropertyValue* Clone() const
{
return new CBoolValue(mValue);
}
};
class CByteValue : public TTypedPropertyValue<s8>
@ -87,6 +107,11 @@ public:
u32 base = (rkString.StartsWith("0x") ? 16 : 10);
mValue = (s8) rkString.ToInt32(base);
}
IPropertyValue* Clone() const
{
return new CByteValue(mValue);
}
};
class CShortValue : public TTypedPropertyValue<s16>
@ -105,27 +130,21 @@ public:
u32 base = (rkString.StartsWith("0x") ? 16 : 10);
mValue = (s16) rkString.ToInt32(base);
}
IPropertyValue* Clone() const
{
return new CShortValue(mValue);
}
};
class CLongValue : public TTypedPropertyValue<s32>
{
protected:
bool mShouldOutputHex;
public:
CLongValue() { mShouldOutputHex = false; mValue = 0; }
CLongValue(s32 Val) { mShouldOutputHex = false; mValue = Val; }
void SetHexStringOutput(bool enable)
{
mShouldOutputHex = enable;
}
CLongValue() { mValue = 0; }
CLongValue(s32 Val) { mValue = Val; }
TString ToString() const
{
if (mShouldOutputHex)
return TString::HexString((u32) mValue, true, true, 8);
else
return TString::FromInt32(mValue, 0, 10);
}
@ -134,6 +153,34 @@ public:
u32 base = (rkString.StartsWith("0x") ? 16 : 10);
mValue = (s32) rkString.ToInt32(base);
}
IPropertyValue* Clone() const
{
return new CLongValue(mValue);
}
};
class CHexLongValue : public TTypedPropertyValue<u32>
{
public:
CHexLongValue() { mValue = 0; }
CHexLongValue(u32 Val) { mValue = Val; }
TString ToString() const
{
return TString::HexString(mValue, true, true, 8);
}
void FromString(const TString& rkString)
{
u32 base = (rkString.StartsWith("0x") ? 16 : 10);
mValue = (s32) rkString.ToInt32(base);
}
IPropertyValue* Clone() const
{
return new CHexLongValue(mValue);
}
};
class CFloatValue : public TTypedPropertyValue<float>
@ -151,6 +198,11 @@ public:
{
mValue = rkString.ToFloat();
}
IPropertyValue* Clone() const
{
return new CFloatValue(mValue);
}
};
class CStringValue : public TTypedPropertyValue<TString>
@ -169,6 +221,11 @@ public:
{
mValue = rkString;
}
IPropertyValue* Clone() const
{
return new CStringValue(mValue);
}
};
class CColorValue : public TTypedPropertyValue<CColor>
@ -207,6 +264,11 @@ public:
pPtr++;
}
}
IPropertyValue* Clone() const
{
return new CColorValue(mValue);
}
};
class CVector3Value : public TTypedPropertyValue<CVector3f>
@ -243,15 +305,26 @@ public:
pPtr++;
}
}
IPropertyValue* Clone() const
{
return new CVector3Value(mValue);
}
};
class CCharacterValue : public TTypedPropertyValue<CAnimationParameters>
{
public:
CCharacterValue() {}
CCharacterValue(const CAnimationParameters& rkParams) { mValue = rkParams; }
TString ToString() const { return ""; }
void FromString(const TString&) { }
IPropertyValue* Clone() const
{
return new CCharacterValue(mValue);
}
};
class CFileValue : public TTypedPropertyValue<CResourceInfo>
@ -262,15 +335,26 @@ public:
TString ToString() const { return ""; }
void FromString(const TString&) { }
IPropertyValue* Clone() const
{
return new CFileValue(mValue);
}
};
class CUnknownValue : public TTypedPropertyValue<std::vector<u8>>
{
public:
CUnknownValue();
CUnknownValue(const std::vector<u8>& rkVec) { mValue = rkVec; }
TString ToString() const { return ""; }
void FromString(const TString&) {}
IPropertyValue* Clone() const
{
return new CUnknownValue(mValue);
}
};
#endif // IPROPERTYVALUE_H

View File

@ -133,7 +133,8 @@ HEADERS += \
PropertyEdit/CPropertyView.h \
PropertyEdit/CPropertyRelay.h \
WorldEditor/CInstancesProxyModel.h \
WorldEditor/CInstancesModel.h
WorldEditor/CInstancesModel.h \
Undo/CEditScriptPropertyCommand.h
# Source Files
SOURCES += \
@ -184,7 +185,8 @@ SOURCES += \
PropertyEdit/CPropertyModel.cpp \
PropertyEdit/CPropertyDelegate.cpp \
PropertyEdit/CPropertyView.cpp \
WorldEditor/CInstancesModel.cpp
WorldEditor/CInstancesModel.cpp \
Undo/CEditScriptPropertyCommand.cpp
# UI Files
FORMS += \

View File

@ -233,6 +233,7 @@ void INodeEditor::ExitPickMode()
void INodeEditor::NotifySelectionModified()
{
RecalculateSelectionBounds();
UpdateSelectionUI();
emit SelectionModified();
}

View File

@ -2,6 +2,7 @@
#include "CPropertyRelay.h"
#include "Editor/UICommon.h"
#include "Editor/Undo/CEditScriptPropertyCommand.h"
#include "Editor/Widgets/WColorPicker.h"
#include "Editor/Widgets/WDraggableSpinBox.h"
#include "Editor/Widgets/WIntegralSpinBox.h"
@ -23,16 +24,24 @@
CPropertyDelegate::CPropertyDelegate(QObject *pParent /*= 0*/)
: QStyledItemDelegate(pParent)
, mpEditor(nullptr)
, mpModel(nullptr)
, mInRelayWidgetEdit(false)
, mEditInProgress(false)
, mRelaysBlocked(false)
{
}
void CPropertyDelegate::SetModel(CPropertyModel *pModel)
void CPropertyDelegate::SetPropertyModel(CPropertyModel *pModel)
{
mpModel = pModel;
}
void CPropertyDelegate::SetEditor(CWorldEditor *pEditor)
{
mpEditor = pEditor;
}
QWidget* CPropertyDelegate::createEditor(QWidget *pParent, const QStyleOptionViewItem& /*rkOption*/, const QModelIndex& rkIndex) const
{
if (!mpModel) return nullptr;
@ -116,6 +125,7 @@ QWidget* CPropertyDelegate::createEditor(QWidget *pParent, const QStyleOptionVie
WResourceSelector *pSelector = new WResourceSelector(pParent);
CFileTemplate *pTemp = static_cast<CFileTemplate*>(pProp->Template());
pSelector->SetAllowedExtensions(pTemp->Extensions());
pSelector->setFont(qobject_cast<QWidget*>(parent())->font()); // bit of a hack to stop the resource selector font from changing
CONNECT_RELAY(pSelector, rkIndex, ResourceChanged(QString))
pOut = pSelector;
@ -190,6 +200,8 @@ void CPropertyDelegate::setEditorData(QWidget *pEditor, const QModelIndex &rkInd
IProperty *pProp = mpModel->PropertyForIndex(rkIndex, false);
if (pProp)
{
if (!mEditInProgress)
{
switch (pProp->Type())
{
@ -210,7 +222,6 @@ void CPropertyDelegate::setEditorData(QWidget *pEditor, const QModelIndex &rkInd
break;
}
case eLongProperty:
{
WIntegralSpinBox *pSpinBox = static_cast<WIntegralSpinBox*>(pEditor);
@ -277,6 +288,7 @@ void CPropertyDelegate::setEditorData(QWidget *pEditor, const QModelIndex &rkInd
}
}
}
// Set editor data for character/bitfield/vector/color sub-property
else if (rkIndex.internalId() & 0x1)
@ -335,9 +347,12 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo
if (!pEditor) return;
IProperty *pProp = mpModel->PropertyForIndex(rkIndex, false);
IPropertyValue *pOldValue = nullptr;
if (pProp)
{
pOldValue = pProp->RawValue()->Clone();
switch (pProp->Type())
{
@ -385,9 +400,6 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo
Color.b = SrcColor.blue() / 255.f;
Color.a = SrcColor.alpha() / 255.f;
pColor->Set(Color);
// Make sure sub-properties update with the new color
mpModel->UpdateSubProperties(rkIndex);
break;
}
@ -430,6 +442,7 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo
else if (rkIndex.internalId() & 0x1)
{
pProp = mpModel->PropertyForIndex(rkIndex, true);
pOldValue = pProp->RawValue()->Clone();
if (pProp->Type() == eCharacterProperty)
SetCharacterModelData(pEditor, rkIndex);
@ -474,13 +487,33 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo
pColor->Set(Value);
}
QModelIndex ParentWidgetIndex = mpModel->index(rkIndex.parent().row(), 1, rkIndex.parent().parent());
mpModel->dataChanged(ParentWidgetIndex, ParentWidgetIndex);
}
}
emit PropertyEdited(rkIndex, true);
if (pProp)
{
// Check for edit in progress
bool Matches = pOldValue->Matches(pProp->RawValue());
if (!Matches && mInRelayWidgetEdit && pEditor->hasFocus())
mEditInProgress = true;
bool EditInProgress = mEditInProgress;
// Check for edit finished
if (!mInRelayWidgetEdit || !pEditor->hasFocus())
mEditInProgress = false;
// Create undo command
if (!Matches || EditInProgress)
{
CEditScriptPropertyCommand *pCommand = new CEditScriptPropertyCommand(mpModel, rkIndex, pOldValue, !mEditInProgress);
mpEditor->UndoStack()->push(pCommand);
}
else
delete pOldValue;
}
}
bool CPropertyDelegate::eventFilter(QObject *pObject, QEvent *pEvent)
@ -513,6 +546,7 @@ QWidget* CPropertyDelegate::CreateCharacterEditor(QWidget *pParent, const QModel
if (Type == eFileProperty)
{
WResourceSelector *pSelector = new WResourceSelector(pParent);
pSelector->setFont(qobject_cast<QWidget*>(parent())->font()); // hack to keep the selector font from changing
if (Params.Version() <= eEchoes)
pSelector->SetAllowedExtensions("ANCS");
@ -636,6 +670,10 @@ void CPropertyDelegate::WidgetEdited(QWidget *pWidget, const QModelIndex& rkInde
{
// This slot is used to update property values as they're being updated so changes can be
// reflected in realtime in other parts of the application.
mInRelayWidgetEdit = true;
if (!mRelaysBlocked)
setModelData(pWidget, mpModel, rkIndex);
mInRelayWidgetEdit = false;
}

View File

@ -1,19 +1,25 @@
#ifndef CPROPERTYDELEGATE_H
#define CPROPERTYDELEGATE_H
#include <QStyledItemDelegate>
#include "CPropertyModel.h"
#include "Editor/WorldEditor/CWorldEditor.h"
#include <QStyledItemDelegate>
class CPropertyDelegate : public QStyledItemDelegate
{
Q_OBJECT
CWorldEditor *mpEditor;
CPropertyModel *mpModel;
bool mInRelayWidgetEdit;
mutable bool mEditInProgress;
mutable bool mRelaysBlocked;
public:
CPropertyDelegate(QObject *pParent = 0);
void SetModel(CPropertyModel *pModel);
void SetPropertyModel(CPropertyModel *pModel);
void SetEditor(CWorldEditor *pEditor);
virtual QWidget* createEditor(QWidget *pParent, const QStyleOptionViewItem& rkOption, const QModelIndex &rkIndex) const;
virtual void setEditorData(QWidget *pEditor, const QModelIndex &rkIndex) const;
virtual void setModelData(QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex &rkIndex) const;
@ -29,9 +35,6 @@ public slots:
protected:
void BlockRelays(bool Block) const { mRelaysBlocked = Block; }
signals:
void PropertyEdited(const QModelIndex& rkIndex, bool IsDone) const;
};
#endif // CPROPERTYDELEGATE_H

View File

@ -292,7 +292,7 @@ QVariant CPropertyModel::data(const QModelIndex& rkIndex, int Role) const
// Add uncooked notification
if (pProp->Template()->CookPreference() == eNeverCook)
{
Text += "<br/><b>This is an uncooked property.</b>";
Text.prepend("<i>[uncooked]</i>");
}
// Add description
@ -393,19 +393,20 @@ Qt::ItemFlags CPropertyModel::flags(const QModelIndex& rkIndex) const
else return (Qt::ItemIsEnabled | Qt::ItemIsEditable);
}
void CPropertyModel::UpdateSubProperties(const QModelIndex& rkIndex)
void CPropertyModel::NotifyPropertyModified(const QModelIndex& rkIndex)
{
IProperty *pProp = PropertyForIndex(rkIndex, false);
if (rowCount(rkIndex) != 0)
emit dataChanged( index(0, 1, rkIndex), index(rowCount(rkIndex) - 1, 1, rkIndex));
if (pProp)
if (rkIndex.internalId() & 0x1)
{
QVector<int> Roles(Qt::DisplayRole);
if (pProp->Type() == eVector3Property)
emit dataChanged( index(0, 1, rkIndex), index(2, 1, rkIndex), Roles);
else if (pProp->Type() == eColorProperty)
emit dataChanged( index(0, 1, rkIndex), index(3, 1, rkIndex), Roles);
QModelIndex Parent = rkIndex.parent();
Parent = Parent.sibling(Parent.row(), 1);
emit dataChanged(Parent, Parent);
}
emit dataChanged(rkIndex, rkIndex);
emit PropertyModified(rkIndex);
}
void CPropertyModel::ResizeArray(const QModelIndex& rkIndex, u32 NewSize)

View File

@ -22,8 +22,12 @@ public:
QModelIndex index(int Row, int Column, const QModelIndex& rkParent) const;
QModelIndex parent(const QModelIndex& rkChild) const;
Qt::ItemFlags flags(const QModelIndex& rkIndex) const;
void UpdateSubProperties(const QModelIndex& rkIndex);
void NotifyPropertyModified(const QModelIndex& rkIndex);
void ResizeArray(const QModelIndex& rkIndex, u32 NewSize);
signals:
void PropertyModified(const QModelIndex& rkIndex);
};
#endif // CPROPERTYMODEL_H

View File

@ -14,16 +14,16 @@ CPropertyView::CPropertyView(QWidget *pParent)
setEditTriggers(AllEditTriggers);
setModel(mpModel);
connect(mpModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(SetPersistentEditors(QModelIndex)));
connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(SetPersistentEditors(QModelIndex)));
connect(mpDelegate, SIGNAL(PropertyEdited(QModelIndex,bool)), this, SLOT(OnPropertyModified(QModelIndex,bool)));
connect(mpModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(SetPersistentEditors(QModelIndex)));
connect(mpModel, SIGNAL(PropertyModified(QModelIndex)), this, SLOT(OnPropertyModified(QModelIndex)));
}
void CPropertyView::setModel(QAbstractItemModel *pModel)
{
CPropertyModel *pPropModel = qobject_cast<CPropertyModel*>(pModel);
mpModel = pPropModel;
mpDelegate->SetModel(pPropModel);
mpDelegate->SetPropertyModel(pPropModel);
QTreeView::setModel(pPropModel);
if (pPropModel)
@ -61,7 +61,13 @@ bool CPropertyView::event(QEvent *pEvent)
else return QTreeView::event(pEvent);
}
void CPropertyView::SetObject(CScriptObject *pObj)
void CPropertyView::SetEditor(CWorldEditor *pEditor)
{
mpEditor = pEditor;
mpDelegate->SetEditor(pEditor);
}
void CPropertyView::SetInstance(CScriptObject *pObj)
{
mpObject = pObj;
mpModel->SetBaseStruct(pObj ? pObj->Properties() : nullptr);
@ -168,23 +174,21 @@ void CPropertyView::ClosePersistentEditors(const QModelIndex& rkIndex)
}
}
void CPropertyView::OnPropertyModified(const QModelIndex &rkIndex, bool IsDone)
void CPropertyView::OnPropertyModified(const QModelIndex& rkIndex)
{
// Check for a resource being changed on a character property. If that's the case we need to remake the persistent editors.
if (rkIndex.internalId() & 0x1)
// Check for a character resource being changed. If that's the case we need to remake the persistent editors.
IProperty *pProp = mpModel->PropertyForIndex(rkIndex, true);
if (pProp->Type() == eCharacterProperty && rkIndex.internalId() & 0x1)
{
if (mpModel->PropertyForIndex(rkIndex, true)->Type() == eCharacterProperty)
{
EGame Game = static_cast<TCharacterProperty*>(mpModel->PropertyForIndex(rkIndex, true))->Get().Version();
EGame Game = static_cast<TCharacterProperty*>(pProp)->Get().Version();
if (mpDelegate->DetermineCharacterPropType(Game, rkIndex) == eFileProperty)
{
QModelIndex Parent = rkIndex.parent();
ClosePersistentEditors(Parent);
SetPersistentEditors(Parent);
}
ClosePersistentEditors(rkIndex.parent());
SetPersistentEditors(rkIndex.parent());
}
}
emit PropertyModified(rkIndex, IsDone);
emit PropertyModified(pProp);
}

View File

@ -10,6 +10,7 @@ class CPropertyView : public QTreeView
{
Q_OBJECT
CWorldEditor *mpEditor;
CPropertyModel *mpModel;
CPropertyDelegate *mpDelegate;
CScriptObject *mpObject;
@ -18,7 +19,8 @@ public:
CPropertyView(QWidget *pParent = 0);
void setModel(QAbstractItemModel *pModel);
bool event(QEvent *pEvent);
void SetObject(CScriptObject *pObj);
void SetEditor(CWorldEditor *pEditor);
void SetInstance(CScriptObject *pObj);
void UpdateEditorProperties(const QModelIndex& rkParent);
inline CPropertyModel* PropertyModel() const { return mpModel; }
@ -26,10 +28,10 @@ public:
public slots:
void SetPersistentEditors(const QModelIndex& rkIndex);
void ClosePersistentEditors(const QModelIndex& rkIndex);
void OnPropertyModified(const QModelIndex& rkIndex, bool IsDone);
void OnPropertyModified(const QModelIndex& rkIndex);
signals:
void PropertyModified(const QModelIndex &rkIndex, bool IsDone);
void PropertyModified(IProperty *pProperty);
};
#endif // CPROPERTYVIEW_H

View File

@ -0,0 +1,53 @@
#include "CEditScriptPropertyCommand.h"
#include "EUndoCommand.h"
CEditScriptPropertyCommand::CEditScriptPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex, IPropertyValue *pOldValue, bool IsDone)
: QUndoCommand("Edit Property")
, mpModel(pModel)
, mIndex(rkIndex)
, mCommandEnded(IsDone)
{
mpProperty = pModel->PropertyForIndex(rkIndex, true);
mpOldValue = pOldValue;
mpNewValue = mpProperty->RawValue()->Clone();
}
CEditScriptPropertyCommand::~CEditScriptPropertyCommand()
{
delete mpOldValue;
delete mpNewValue;
}
int CEditScriptPropertyCommand::id() const
{
return eEditScriptPropertyCmd;
}
bool CEditScriptPropertyCommand::mergeWith(const QUndoCommand *pkOther)
{
if (!mCommandEnded && pkOther->id() == eEditScriptPropertyCmd)
{
const CEditScriptPropertyCommand *pkCmd = static_cast<const CEditScriptPropertyCommand*>(pkOther);
if (pkCmd->mpProperty == mpProperty)
{
mpNewValue->Copy(pkCmd->mpNewValue);
mCommandEnded = pkCmd->mCommandEnded;
return true;
}
}
return false;
}
void CEditScriptPropertyCommand::undo()
{
mpProperty->RawValue()->Copy(mpOldValue);
mpModel->NotifyPropertyModified(mIndex);
}
void CEditScriptPropertyCommand::redo()
{
mpProperty->RawValue()->Copy(mpNewValue);
mpModel->NotifyPropertyModified(mIndex);
}

View File

@ -0,0 +1,26 @@
#ifndef CEDITSCRIPTPROPERTYCOMMAND_H
#define CEDITSCRIPTPROPERTYCOMMAND_H
#include "Editor/PropertyEdit/CPropertyDelegate.h"
#include <QUndoCommand>
class CEditScriptPropertyCommand : public QUndoCommand
{
CPropertyModel *mpModel;
IProperty *mpProperty;
QModelIndex mIndex;
IPropertyValue *mpOldValue;
IPropertyValue *mpNewValue;
bool mCommandEnded;
public:
CEditScriptPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex, IPropertyValue *pOldValue, bool IsDone);
~CEditScriptPropertyCommand();
int id() const;
bool mergeWith(const QUndoCommand *pkOther);
void undo();
void redo();
};
#endif // CEDITSCRIPTPROPERTYCOMMAND_H

View File

@ -5,7 +5,8 @@ enum EUndoCommand
{
eTranslateNodeCmd,
eRotateNodeCmd,
eScaleNodeCmd
eScaleNodeCmd,
eEditScriptPropertyCmd
};
#endif // EUNDOCOMMAND

View File

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

View File

@ -106,6 +106,22 @@ bool WResourceSelector::HasSupportedExtension(const CResourceInfo& rkRes)
return IsSupportedExtension(TO_QSTRING(rkRes.Type().ToString()));
}
void WResourceSelector::UpdateFrameColor()
{
bool RedFrame = false;
// Red frame should only display if an incorrect resource path is entered. It shouldn't display on Invalid Asset ID.
if (!mResourceValid)
{
TString Name = mResource.ToString().GetFileName(false);
if (!Name.IsHexString() || (Name.Size() != 8 && Name.Size() != 16) || mResource.ID().IsValid())
RedFrame = true;
}
mUI.LineEdit->setStyleSheet(RedFrame ? "border: 1px solid red" : "");
mUI.LineEdit->setFont(font());
}
// ************ GETTERS ************
CResourceInfo WResourceSelector::GetResourceInfo()
{
@ -147,24 +163,38 @@ void WResourceSelector::SetResource(CResource *pRes)
SetResource(CResourceInfo());
}
void WResourceSelector::SetResource(const QString& rkRes)
{
TString Res = TO_TSTRING(rkRes);
TString Name = Res.GetFileName(false);
TString Dir = Res.GetFileDirectory();
TString Ext = Res.GetFileExtension();
if (Dir.IsEmpty() && Name.IsHexString() && (Name.Size() == 8 || Name.Size() == 16) && Ext.Size() == 4)
SetResource(CResourceInfo(Name.Size() == 8 ? Name.ToInt32() : Name.ToInt64(), Ext));
else
SetResource(CResourceInfo(Res));
}
void WResourceSelector::SetResource(const CResourceInfo& rkRes)
{
if (mResource != rkRes)
{
mResource = rkRes;
if (mResource.IsValid())
{
mResourceValid = HasSupportedExtension(rkRes);
mUI.LineEdit->setText(TO_QSTRING(mResource.ToString()));
}
else
{
mResourceValid = false;
mUI.LineEdit->clear();
}
mUI.LineEdit->setText(TO_QSTRING(mResource.ToString()));
UpdateFrameColor();
CreatePreviewPanel();
SetButtonsBasedOnResType();
Q_ASSERT(!mUI.LineEdit->text().isEmpty());
emit ResourceChanged(TO_QSTRING(mResource.ToString()));
}
}
void WResourceSelector::SetAllowedExtensions(const QString& extension)
@ -183,7 +213,7 @@ void WResourceSelector::SetAllowedExtensions(const TStringList& extensions)
void WResourceSelector::SetText(const QString& ResPath)
{
mUI.LineEdit->setText(ResPath);
LoadResource(ResPath);
SetResource(ResPath);
}
void WResourceSelector::SetEditButtonEnabled(bool Enabled)
@ -214,7 +244,7 @@ void WResourceSelector::AdjustPreviewToParent(bool adjust)
// ************ SLOTS ************
void WResourceSelector::OnLineEditTextEdited()
{
LoadResource(mUI.LineEdit->text());
SetResource(mUI.LineEdit->text());
}
void WResourceSelector::OnBrowseButtonClicked()
@ -246,7 +276,7 @@ void WResourceSelector::OnBrowseButtonClicked()
if (!NewRes.isEmpty())
{
mUI.LineEdit->setText(NewRes);
LoadResource(NewRes);
SetResource(NewRes);
}
}
@ -273,27 +303,6 @@ void WResourceSelector::Export()
emit ExportResource(mResource);
}
void WResourceSelector::LoadResource(const QString& ResPath)
{
mResource = CResourceInfo();
TString PathStr = ResPath.toStdString();
TString Ext = PathStr.GetFileExtension();
if (IsSupportedExtension(TO_QSTRING(Ext)))
{
mResource = CResourceInfo(TO_TSTRING(ResPath));
mResourceValid = mResource.IsValid();
if (mPreviewPanelValid) mpPreviewPanel->SetResource(mResource.Load());
}
else mResourceValid = false;
SetButtonsBasedOnResType();
CreatePreviewPanel();
emit ResourceChanged(ResPath);
}
void WResourceSelector::CreatePreviewPanel()
{
delete mpPreviewPanel;

View File

@ -54,6 +54,7 @@ public:
bool eventFilter(QObject *, QEvent *);
bool IsSupportedExtension(const QString& extension);
bool HasSupportedExtension(const CResourceInfo& rkRes);
void UpdateFrameColor();
// Getters
CResourceInfo GetResourceInfo();
@ -65,6 +66,7 @@ public:
// Setters
void SetResource(CResource *pRes);
void SetResource(const QString& rkRes);
void SetResource(const CResourceInfo& rkRes);
void SetAllowedExtensions(const QString& extension);
void SetAllowedExtensions(const QStringList& extensions);
@ -85,7 +87,6 @@ public slots:
private:
void Edit();
void Export();
void LoadResource(const QString& ResPath);
void CreatePreviewPanel();
void ShowPreviewPanel();
void HidePreviewPanel();

View File

@ -108,6 +108,7 @@ void WVectorEditor::SetX(double x)
mpSpinBoxX->setValue((double) x);
mpSpinBoxX->blockSignals(false);
mEditing = true;
emit ValueChanged(mValue);
}
@ -119,6 +120,7 @@ void WVectorEditor::SetY(double y)
mpSpinBoxY->setValue((double) y);
mpSpinBoxY->blockSignals(false);
mEditing = true;
emit ValueChanged(mValue);
}
@ -130,6 +132,7 @@ void WVectorEditor::SetZ(double z)
mpSpinBoxZ->setValue((double) z);
mpSpinBoxZ->blockSignals(false);
mEditing = true;
emit ValueChanged(mValue);
}
@ -181,5 +184,6 @@ void WVectorEditor::SetupUI()
// ************ PRIVATE SLOTS ************
void WVectorEditor::OnSpinBoxEditingDone()
{
emit EditingDone(mValue);
if (mEditing) emit EditingDone(mValue);
mEditing = false;
}

View File

@ -15,6 +15,7 @@ class WVectorEditor : public QWidget
Q_OBJECT
CVector3f mValue;
bool mEditing;
Qt::Orientation mOrientation;
QLayout *mpLayout;

View File

@ -16,7 +16,7 @@ WModifyTab::WModifyTab(QWidget *pParent) :
ui->PropertyView->header()->resizeSection(0, PropViewWidth * 0.3);
ui->PropertyView->header()->resizeSection(1, PropViewWidth * 0.3);
ui->PropertyView->header()->setSectionResizeMode(1, QHeaderView::Fixed);
connect(ui->PropertyView, SIGNAL(PropertyModified(QModelIndex,bool)), this, SLOT(OnPropertyModified(QModelIndex,bool)));
connect(ui->PropertyView, SIGNAL(PropertyModified(IProperty*)), this, SLOT(OnPropertyModified(IProperty*)));
mpInLinkModel = new CLinkModel(this);
mpInLinkModel->SetConnectionType(CLinkModel::eIncoming);
@ -41,6 +41,7 @@ WModifyTab::~WModifyTab()
void WModifyTab::SetEditor(CWorldEditor *pEditor)
{
mpWorldEditor = pEditor;
ui->PropertyView->SetEditor(mpWorldEditor);
connect(mpWorldEditor, SIGNAL(SelectionTransformed()), this, SLOT(OnWorldSelectionTransformed()));
}
@ -60,7 +61,7 @@ void WModifyTab::GenerateUI(QList<CSceneNode*>& Selection)
CScriptObject *pObj = pScriptNode->Object();
// Set up UI
ui->PropertyView->SetObject(pObj);
ui->PropertyView->SetInstance(pObj);
mpInLinkModel->SetObject(pObj);
mpOutLinkModel->SetObject(pObj);
ui->LightGroupBox->hide();
@ -75,7 +76,7 @@ void WModifyTab::GenerateUI(QList<CSceneNode*>& Selection)
void WModifyTab::ClearUI()
{
ui->ObjectsTabWidget->hide();
ui->PropertyView->SetObject(nullptr);
ui->PropertyView->SetInstance(nullptr);
ui->LightGroupBox->hide();
mpSelectedNode = nullptr;
}
@ -85,17 +86,27 @@ void WModifyTab::OnWorldSelectionTransformed()
ui->PropertyView->UpdateEditorProperties(QModelIndex());
}
void WModifyTab::OnPropertyModified(const QModelIndex& rkIndex, bool /*IsDone*/)
void WModifyTab::OnPropertyModified(IProperty *pProp)
{
if (mpSelectedNode->NodeType() == eScriptNode)
{
CScriptNode *pNode = static_cast<CScriptNode*>(mpSelectedNode);
IProperty *pProperty = ui->PropertyView->PropertyModel()->PropertyForIndex(rkIndex, true);
pNode->PropertyModified(pProperty);
pNode->PropertyModified(pProp);
// If this is the instance name property, then other parts of the UI need to be updated to reflect the new name.
if (pNode->Object()->IsEditorProperty(pProperty) && pProperty->Type() == eStringProperty)
if (pNode->Object()->IsEditorProperty(pProp) && pProp->Type() == eStringProperty)
mpWorldEditor->UpdateSelectionUI();
// If this is a model/character, then we'll treat it as a modified selection. This is to make sure the selection bounds updates.
if (pProp->Type() == eFileProperty)
{
CFileTemplate *pFile = static_cast<CFileTemplate*>(pProp->Template());
if (pFile->AcceptsExtension("CMDL") || pFile->AcceptsExtension("ANCS") || pFile->AcceptsExtension("CHAR"))
mpWorldEditor->NotifySelectionModified();
}
else if (pProp->Type() == eCharacterProperty)
mpWorldEditor->NotifySelectionModified();
}
}

View File

@ -36,7 +36,7 @@ public:
public slots:
void OnWorldSelectionTransformed();
void OnPropertyModified(const QModelIndex& rkIndex, bool IsDone);
void OnPropertyModified(IProperty *pProp);
private:
Ui::WModifyTab *ui;