From 4faadbda613e39a2cfafc38db0057555a0d93feb Mon Sep 17 00:00:00 2001 From: Aruki Date: Sun, 8 Jul 2018 21:59:01 -0600 Subject: [PATCH] Fixed a lot of property bugs, fixed more various VS2017 compiler errors, property editor works correctly now --- src/Common/FileIO/CVectorOutStream.h | 2 +- src/Common/TString.h | 41 ++- src/Core/Resource/CMaterial.h | 2 +- src/Core/Resource/Model/CVertex.h | 2 +- src/Core/Resource/Script/CScriptObject.cpp | 3 + src/Core/Resource/Script/IPropertyNew.cpp | 19 +- src/Core/Resource/Script/IPropertyNew.h | 12 +- .../Resource/Script/Property/CArrayProperty.h | 14 +- .../Resource/Script/Property/CAssetProperty.h | 6 + .../Resource/Script/Property/CEnumProperty.h | 7 + .../Resource/Script/Property/CFlagsProperty.h | 19 + .../Resource/Script/Property/CFloatProperty.h | 2 +- .../Resource/Script/Property/CIntProperty.h | 2 +- .../Script/Property/CStructProperty.h | 8 +- .../Resource/Script/Property/TPropertyRef.h | 2 +- src/Core/Scene/CScriptNode.cpp | 8 +- src/Editor/ModelEditor/CModelEditorWindow.cpp | 8 +- src/Editor/PropertyEdit/CPropertyDelegate.cpp | 342 ++++++++---------- src/Editor/PropertyEdit/CPropertyModel.cpp | 32 +- src/Editor/PropertyEdit/CPropertyView.cpp | 2 +- .../ResourceBrowser/CResourceBrowser.cpp | 5 + src/Editor/Undo/CEditScriptPropertyCommand.h | 172 +++++++-- src/Editor/Widgets/CSoftValidatorLineEdit.h | 4 +- src/Editor/WorldEditor/CWorldEditor.cpp | 38 +- src/Editor/WorldEditor/CWorldEditor.h | 2 +- src/Editor/WorldEditor/WEditorProperties.cpp | 2 +- src/Editor/main.cpp | 2 +- src/Math/CMatrix4f.cpp | 10 - src/Math/CMatrix4f.h | 10 + src/Math/CVector3f.cpp | 2 +- 30 files changed, 466 insertions(+), 314 deletions(-) diff --git a/src/Common/FileIO/CVectorOutStream.h b/src/Common/FileIO/CVectorOutStream.h index 846b0c1f..7f0881ce 100644 --- a/src/Common/FileIO/CVectorOutStream.h +++ b/src/Common/FileIO/CVectorOutStream.h @@ -6,7 +6,7 @@ class CVectorOutStream : public IOutputStream { - static const u32 skAllocSize = 1024; // must be power of 2 + static const u32 skAllocSize = 1; // must be 1 or a power of 2 std::vector *mpVector; bool mOwnsVector; diff --git a/src/Common/TString.h b/src/Common/TString.h index d3802806..cb8b5bbb 100644 --- a/src/Common/TString.h +++ b/src/Common/TString.h @@ -932,7 +932,46 @@ public: static _TString FromFloat(float Value, int MinDecimals = 1) { - return Format("%f.*", Value, MinDecimals); + // Initial float -> string conversion + std::basic_stringstream SStream; + if (MinDecimals > 0) SStream.setf(std::ios_base::showpoint); + SStream.setf(std::ios_base::fixed, std::ios_base::floatfield); + SStream << Value; + _TString Out = SStream.str(); + + // Make sure we have the right number of decimals + int DecIdx = Out.IndexOf(CHAR_LITERAL('.')); + + if (DecIdx == -1 && MinDecimals > 0) + { + DecIdx = Out.Size(); + Out.Append(CHAR_LITERAL('.')); + } + + int NumZeroes = (DecIdx == -1 ? 0 : Out.Size() - (DecIdx + 1)); + + // Add extra zeroes to meet the minimum decimal count + if (NumZeroes < MinDecimals) + { + for (int iDec = 0; iDec < (MinDecimals - NumZeroes); iDec++) + Out.Append(CHAR_LITERAL('.')); + } + + // Remove unnecessary trailing zeroes from the end of the string + else if (NumZeroes > MinDecimals) + { + while (Out.Back() == CHAR_LITERAL('0') && NumZeroes > MinDecimals && NumZeroes > 0) + { + Out = Out.ChopBack(1); + NumZeroes--; + } + + // Remove decimal point + if (NumZeroes == 0) + Out = Out.ChopBack(1); + } + + return Out; } static _TString FileSizeString(u64 Size, u32 NumDecimals = 2) diff --git a/src/Core/Resource/CMaterial.h b/src/Core/Resource/CMaterial.h index a8498b75..e87bf056 100644 --- a/src/Core/Resource/CMaterial.h +++ b/src/Core/Resource/CMaterial.h @@ -112,7 +112,7 @@ public: inline void SetOptions(FMaterialOptions Options) { mOptions = Options; Update(); } inline void SetVertexDescription(FVertexDescription Desc) { mVtxDesc = Desc; Update(); } inline void SetBlendMode(GLenum SrcFac, GLenum DstFac) { mBlendSrcFac = SrcFac; mBlendDstFac = DstFac; mRecalcHash = true; } - inline void SetKonst(CColor& Konst, u32 KIndex) { mKonstColors[KIndex] = Konst; Update(); } + inline void SetKonst(const CColor& Konst, u32 KIndex) { mKonstColors[KIndex] = Konst; Update(); } inline void SetIndTexture(CTexture *pTex) { mpIndirectTexture = pTex; } inline void SetLightingEnabled(bool Enabled) { mLightingEnabled = Enabled; Update(); } diff --git a/src/Core/Resource/Model/CVertex.h b/src/Core/Resource/Model/CVertex.h index ee221828..041061b1 100644 --- a/src/Core/Resource/Model/CVertex.h +++ b/src/Core/Resource/Model/CVertex.h @@ -24,7 +24,7 @@ public: CVertex() {} - CVertex(CVector3f& rPos) + CVertex(const CVector3f& rPos) { Position = rPos; } diff --git a/src/Core/Resource/Script/CScriptObject.cpp b/src/Core/Resource/Script/CScriptObject.cpp index e73c9c2e..01a85428 100644 --- a/src/Core/Resource/Script/CScriptObject.cpp +++ b/src/Core/Resource/Script/CScriptObject.cpp @@ -88,6 +88,9 @@ bool CScriptObject::IsEditorProperty(IPropertyNew *pProp) (pProp == mScale.Property()) || (pProp == mActive.Property()) || (pProp == mLightParameters.Property()) || + (pProp->Parent() == mPosition.Property()) || + (pProp->Parent() == mRotation.Property()) || + (pProp->Parent() == mScale.Property()) || (pProp->Parent() == mLightParameters.Property()) ); } diff --git a/src/Core/Resource/Script/IPropertyNew.cpp b/src/Core/Resource/Script/IPropertyNew.cpp index 887eb407..40250b41 100644 --- a/src/Core/Resource/Script/IPropertyNew.cpp +++ b/src/Core/Resource/Script/IPropertyNew.cpp @@ -55,11 +55,6 @@ void IPropertyNew::_CalcOffset() } } -u32 IPropertyNew::_GetOffset() const -{ - return mOffset; -} - void IPropertyNew::_ClearChildren() { for (int ChildIdx = 0; ChildIdx < mChildren.size(); ChildIdx++) @@ -118,7 +113,7 @@ void IPropertyNew::InitFromArchetype(IPropertyNew* pOther) { //@todo maybe somehow use Serialize for this instead? mpArchetype = pOther; - mFlags = pOther->mFlags & ~EPropertyFlag::ArchetypeCopyFlags; + mFlags = pOther->mFlags & EPropertyFlag::ArchetypeCopyFlags; mID = pOther->mID; mName = pOther->mName; mDescription = pOther->mDescription; @@ -161,7 +156,8 @@ void* IPropertyNew::RawValuePtr(void* pData) const return pData; void* pBasePtr = (mpPointerParent ? mpPointerParent->GetChildDataPointer(pData) : pData); - return ((char*)pBasePtr + mOffset); + void* pValuePtr = ((char*)pBasePtr + mOffset); + return pValuePtr; } IPropertyNew* IPropertyNew::ChildByID(u32 ID) const @@ -342,8 +338,7 @@ IPropertyNew* IPropertyNew::Create(EPropertyTypeNew Type, } IPropertyNew* IPropertyNew::CreateCopy(IPropertyNew* pArchetype, - IPropertyNew* pParent, - bool CallPostInit /*= true*/) + IPropertyNew* pParent) { // Note this is mainly going to be used to create copies from struct/enum/flag archetype properties. // Properties that have archetypes will never be the root property of a script template, and there @@ -354,11 +349,5 @@ IPropertyNew* IPropertyNew::CreateCopy(IPropertyNew* pArchetype, IPropertyNew* pOut = Create(pArchetype->Type(), pParent, pParent->mpMasterTemplate, pParent->mpScriptTemplate, false); pOut->InitFromArchetype(pArchetype); pArchetype->mSubInstances.push_back(pOut); - - if (CallPostInit) - { - pOut->PostInitialize(); - } - return pOut; } diff --git a/src/Core/Resource/Script/IPropertyNew.h b/src/Core/Resource/Script/IPropertyNew.h index 916ca63e..5f332611 100644 --- a/src/Core/Resource/Script/IPropertyNew.h +++ b/src/Core/Resource/Script/IPropertyNew.h @@ -157,7 +157,6 @@ protected: /** Private constructor - use static methods to instantiate */ IPropertyNew(); void _CalcOffset(); - u32 _GetOffset() const; void _ClearChildren(); /** Called after property is created and fully initialized */ @@ -213,6 +212,7 @@ public: inline TString Description() const; inline TString Suffix() const; inline TIDString IDString(bool FullyQualified) const; + inline u32 Offset() const; inline u32 ID() const; inline bool IsArchetype() const { return mFlags.HasFlag(EPropertyFlag::IsArchetype); } @@ -227,8 +227,7 @@ public: bool CallPostInit = true); static IPropertyNew* CreateCopy(IPropertyNew* pArchetype, - IPropertyNew* pParent, - bool CallPostInit = true); + IPropertyNew* pParent); }; inline ECookPreferenceNew IPropertyNew::CookPreference() const @@ -298,12 +297,17 @@ inline TString IPropertyNew::Suffix() const inline TString IPropertyNew::IDString(bool FullyQualified) const { - if (FullyQualified && mpParent != nullptr) + if (FullyQualified && mpParent != nullptr && mpParent->Parent() != nullptr) return mpParent->IDString(FullyQualified) + ":" + TString::HexString(mID); else return TString::HexString(mID); } +inline u32 IPropertyNew::Offset() const +{ + return mOffset; +} + inline u32 IPropertyNew::ID() const { return mID; diff --git a/src/Core/Resource/Script/Property/CArrayProperty.h b/src/Core/Resource/Script/Property/CArrayProperty.h index b01fa79e..2a5b2bd9 100644 --- a/src/Core/Resource/Script/Property/CArrayProperty.h +++ b/src/Core/Resource/Script/Property/CArrayProperty.h @@ -24,8 +24,9 @@ class CArrayProperty : public TTypedPropertyNew { friend class CTemplateLoader; /** This class inherits from TTypedPropertyNew in order to expose the array - * count value. Outside users can edit this value and we respond by updating the - * allocated space, handling destruction/construction, etc. + * count value (the first member of SScriptArray). Outside users can edit this + * value and we respond by updating the allocated space, handling item destruction + * and construction, etc. */ IPropertyNew* mpItemArchetype; @@ -99,7 +100,7 @@ public: virtual void SerializeValue(void* pData, IArchive& Arc) const { u32 Count = ArrayCount(pData); - Arc.SerializePrimitive(Count); + Arc.SerializeContainerSize(Count, "ArrayElement"); if (Arc.IsReader()) Resize(pData, Count); @@ -115,6 +116,13 @@ public: } } + virtual void InitFromArchetype(IPropertyNew* pOther) + { + TTypedPropertyNew::InitFromArchetype(pOther); + CArrayProperty* pOtherArray = static_cast(pOther); + mpItemArchetype = IPropertyNew::CreateCopy(pOtherArray->mpItemArchetype, this); + } + u32 ArrayCount(void* pPropertyData) const { return ValueRef(pPropertyData); diff --git a/src/Core/Resource/Script/Property/CAssetProperty.h b/src/Core/Resource/Script/Property/CAssetProperty.h index 1d73da96..6e1a4960 100644 --- a/src/Core/Resource/Script/Property/CAssetProperty.h +++ b/src/Core/Resource/Script/Property/CAssetProperty.h @@ -18,6 +18,12 @@ public: } #endif + virtual void InitFromArchetype(IPropertyNew* pOther) + { + TTypedPropertyNew::InitFromArchetype(pOther); + mTypeFilter = static_cast(pOther)->mTypeFilter; + } + virtual void SerializeValue(void* pData, IArchive& Arc) const { Arc.SerializePrimitive( ValueRef(pData) ); diff --git a/src/Core/Resource/Script/Property/CEnumProperty.h b/src/Core/Resource/Script/Property/CEnumProperty.h index 7d05eb26..533b47a9 100644 --- a/src/Core/Resource/Script/Property/CEnumProperty.h +++ b/src/Core/Resource/Script/Property/CEnumProperty.h @@ -46,6 +46,13 @@ public: Arc.SerializePrimitive( (u32&) ValueRef(pData) ); } + virtual void InitFromArchetype(IPropertyNew* pOther) + { + TTypedPropertyNew::InitFromArchetype(pOther); + TEnumPropertyBase* pOtherEnum = static_cast(pOther); + mValues = pOtherEnum->mValues; + } + virtual TString GetTemplateFileName() { ASSERT(IsArchetype() || mpArchetype); diff --git a/src/Core/Resource/Script/Property/CFlagsProperty.h b/src/Core/Resource/Script/Property/CFlagsProperty.h index 9a242bd6..71ae6613 100644 --- a/src/Core/Resource/Script/Property/CFlagsProperty.h +++ b/src/Core/Resource/Script/Property/CFlagsProperty.h @@ -75,11 +75,30 @@ public: } #endif + virtual void PostInitialize() + { + TTypedPropertyNew::PostInitialize(); + + // Create AllFlags mask + mAllFlags = 0; + + for (int FlagIdx = 0; FlagIdx < mBitFlags.size(); FlagIdx++) + mAllFlags |= mBitFlags[FlagIdx].Mask; + } + virtual void SerializeValue(void* pData, IArchive& rArc) const { rArc.SerializeHexPrimitive( (u32&) ValueRef(pData) ); } + virtual void InitFromArchetype(IPropertyNew* pOther) + { + TTypedPropertyNew::InitFromArchetype(pOther); + CFlagsProperty* pOtherFlags = static_cast(pOther); + mBitFlags = pOtherFlags->mBitFlags; + mAllFlags = pOtherFlags->mAllFlags; + } + virtual TString GetTemplateFileName() { ASSERT(IsArchetype() || mpArchetype); diff --git a/src/Core/Resource/Script/Property/CFloatProperty.h b/src/Core/Resource/Script/Property/CFloatProperty.h index 2dd98bb5..ba310938 100644 --- a/src/Core/Resource/Script/Property/CFloatProperty.h +++ b/src/Core/Resource/Script/Property/CFloatProperty.h @@ -18,7 +18,7 @@ public: Arc.SerializePrimitive( (float&) ValueRef(pData) ); } - virtual TString ValueAsString(void* pData) + virtual TString ValueAsString(void* pData) const { return TString::FromFloat( Value(pData) ); } diff --git a/src/Core/Resource/Script/Property/CIntProperty.h b/src/Core/Resource/Script/Property/CIntProperty.h index a26c7fe3..68effd44 100644 --- a/src/Core/Resource/Script/Property/CIntProperty.h +++ b/src/Core/Resource/Script/Property/CIntProperty.h @@ -18,7 +18,7 @@ public: Arc.SerializePrimitive( (u32&) ValueRef(pData) ); } - virtual TString ValueAsString(void* pData) + virtual TString ValueAsString(void* pData) const { return TString::FromInt32( Value(pData), 0, 10 ); } diff --git a/src/Core/Resource/Script/Property/CStructProperty.h b/src/Core/Resource/Script/Property/CStructProperty.h index 0a80eeb5..442582cf 100644 --- a/src/Core/Resource/Script/Property/CStructProperty.h +++ b/src/Core/Resource/Script/Property/CStructProperty.h @@ -26,7 +26,7 @@ public: if (!mChildren.empty()) { IPropertyNew* pLastChild = mChildren.back(); - return _GetOffset() + pLastChild->DataSize(); + return (pLastChild->Offset() - Offset()) + pLastChild->DataSize(); } else { @@ -36,7 +36,11 @@ public: virtual u32 DataAlignment() const { - return (mChildren.empty() ? 1 : mChildren[0]->DataAlignment()); + // TODO. Should be aligned with the first child, but this function is called before children are loaded. + // So for now just use 8 to ensure correct alignment for all child types, but this is wasteful... + return 8; + + //return (mChildren.empty() ? 1 : mChildren[0]->DataAlignment()); } virtual void Construct(void* pData) const diff --git a/src/Core/Resource/Script/Property/TPropertyRef.h b/src/Core/Resource/Script/Property/TPropertyRef.h index 8719f2f4..d6ea66ec 100644 --- a/src/Core/Resource/Script/Property/TPropertyRef.h +++ b/src/Core/Resource/Script/Property/TPropertyRef.h @@ -50,7 +50,7 @@ public: /** Accessors */ inline CScriptObject* Object() const { return mpObject; } inline PropertyClass* Property() const { return mpProperty; } - inline ValueType Get() const { ASSERT(IsValid()); return *((ValueType*) mpProperty->RawValuePtr( mpObject->PropertyData() )); } + inline ValueType Get() const { return IsValid() ? *((ValueType*) mpProperty->RawValuePtr( mpObject->PropertyData() )) : ValueType(); } inline void Set(const ValueType& kIn) const { if (IsValid()) *((ValueType*) mpProperty->RawValuePtr( mpObject->PropertyData() )) = kIn; } inline bool IsValid() const { return mpObject != nullptr && mpProperty != nullptr; } diff --git a/src/Core/Scene/CScriptNode.cpp b/src/Core/Scene/CScriptNode.cpp index 60029ae8..03fa3ce2 100644 --- a/src/Core/Scene/CScriptNode.cpp +++ b/src/Core/Scene/CScriptNode.cpp @@ -496,13 +496,13 @@ void CScriptNode::PropertyModified(IPropertyNew* pProp) if (pProp == pTemplate->NameProperty()) SetName("[" + mpInstance->Template()->Name() + "] " + mpInstance->InstanceName()); - else if (pProp == pTemplate->PositionProperty()) + else if (pProp == pTemplate->PositionProperty() || pProp->Parent() == pTemplate->PositionProperty()) mPosition = mpInstance->Position(); - else if (pProp == pTemplate->RotationProperty()) + else if (pProp == pTemplate->RotationProperty() || pProp->Parent() == pTemplate->RotationProperty()) mRotation = CQuaternion::FromEuler(mpInstance->Rotation()); - else if (pProp == pTemplate->ScaleProperty()) + else if (pProp == pTemplate->ScaleProperty() || pProp->Parent() == pTemplate->ScaleProperty()) mScale = mpInstance->Scale(); MarkTransformChanged(); @@ -560,7 +560,7 @@ void CScriptNode::GeneratePosition() if (!mHasValidPosition) { // Default to center of the active area; this is to prevent recursion issues - CTransform4f& AreaTransform = mpScene->ActiveArea()->Transform(); + CTransform4f AreaTransform = mpScene->ActiveArea()->Transform(); mPosition = CVector3f(AreaTransform[0][3], AreaTransform[1][3], AreaTransform[2][3]); mHasValidPosition = true; MarkTransformChanged(); diff --git a/src/Editor/ModelEditor/CModelEditorWindow.cpp b/src/Editor/ModelEditor/CModelEditorWindow.cpp index bd7d03a7..732ecc7d 100644 --- a/src/Editor/ModelEditor/CModelEditorWindow.cpp +++ b/src/Editor/ModelEditor/CModelEditorWindow.cpp @@ -764,9 +764,10 @@ void CModelEditorWindow::ConvertToDDS() if (Input.isEmpty()) return; TString TexFilename = TO_TSTRING(Input); - CTexture *pTex = CTextureDecoder::LoadDDS( CFileInStream(TexFilename, IOUtil::eLittleEndian), nullptr ); - TString OutName = TexFilename.GetFilePathWithoutExtension() + ".dds"; + CFileInStream InTextureFile(TexFilename, IOUtil::eLittleEndian); + CTexture *pTex = CTextureDecoder::LoadTXTR( InTextureFile, nullptr ); + TString OutName = TexFilename.GetFilePathWithoutExtension() + ".dds"; CFileOutStream Out(OutName, IOUtil::eLittleEndian); if (!Out.IsValid()) QMessageBox::warning(this, "Error", "Couldn't open output DDS!"); @@ -786,7 +787,8 @@ void CModelEditorWindow::ConvertToTXTR() if (Input.isEmpty()) return; TString TexFilename = TO_TSTRING(Input); - CTexture *pTex = CTextureDecoder::LoadDDS(CFileInStream(TexFilename, IOUtil::eLittleEndian), nullptr); + CFileInStream InTextureFile = CFileInStream(TexFilename, IOUtil::eLittleEndian); + CTexture *pTex = CTextureDecoder::LoadDDS(InTextureFile, nullptr); TString OutName = TexFilename.GetFilePathWithoutExtension() + ".txtr"; if ((pTex->TexelFormat() != eDXT1) || (pTex->NumMipMaps() > 1)) diff --git a/src/Editor/PropertyEdit/CPropertyDelegate.cpp b/src/Editor/PropertyEdit/CPropertyDelegate.cpp index 0d2f6a8e..4405b244 100644 --- a/src/Editor/PropertyEdit/CPropertyDelegate.cpp +++ b/src/Editor/PropertyEdit/CPropertyDelegate.cpp @@ -354,97 +354,134 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo if (!mpModel) return; if (!pEditor) return; - //FIXME -/* IPropertyNew *pProp = mpModel->PropertyForIndex(rkIndex, false); - bool HadEditInProgress = mEditInProgress; - mEditInProgress = mInRelayWidgetEdit && (pEditor->hasFocus() || pProp->Type() == EPropertyTypeNew::Color); - bool EditJustFinished = (!mEditInProgress && HadEditInProgress); - - bool Matches = false; - IUndoCommand* pCommand = nullptr; + IEditPropertyCommand* pCommand = nullptr; + IPropertyNew *pProp = mpModel->PropertyForIndex(rkIndex, true); + void* pData = mpModel->GetPropertyData(); if (pProp) { - switch (pProp->Type()) - { + EPropertyTypeNew Type = pProp->Type(); - case EPropertyTypeNew::Bool: + if (Type != EPropertyTypeNew::Array) { - QCheckBox *pCheckBox = static_cast(pEditor); - bool NewValue = - pCommand = new TEditScriptPropertyCommand(pProp, mpModel->GetScriptObject(), mpEditor, pCheckBox->isChecked(), - bool NewValue = TEditScriptPropertyCommand(pProp, - TBoolProperty *pBool = static_cast(pProp); - pBool->Set(pCheckBox->isChecked()); - break; + // TODO: support this for non script object properties + pCommand = new CEditScriptPropertyCommand(mpEditor, mpModel->GetScriptObject(), pProp); + pCommand->SaveOldData(); + + // Handle sub-properties of flags and animation sets + if (rkIndex.internalId() & 0x80000000) + { + if (pProp->Type() == EPropertyTypeNew::AnimationSet) + SetCharacterModelData(pEditor, rkIndex); + + else if (pProp->Type() == EPropertyTypeNew::Flags) + { + QCheckBox* pCheckBox = static_cast(pEditor); + CFlagsProperty* pFlags = static_cast(pProp); + u32 Mask = pFlags->FlagMask(rkIndex.row()); + + int Flags = pFlags->Value(pData); + if (pCheckBox->isChecked()) Flags |= Mask; + else Flags &= ~Mask; + pFlags->ValueRef(pData) = Flags; + } + } + + else + { + switch (pProp->Type()) + { + + case EPropertyTypeNew::Bool: + { + QCheckBox *pCheckBox = static_cast(pEditor); + CBoolProperty* pBool = static_cast(pProp); + pBool->ValueRef(pData) = pCheckBox->isChecked(); + break; + } + + case EPropertyTypeNew::Short: + { + WIntegralSpinBox* pSpinBox = static_cast(pEditor); + CShortProperty* pShort = static_cast(pProp); + pShort->ValueRef(pData) = pSpinBox->value(); + break; + } + + case EPropertyTypeNew::Int: + { + WIntegralSpinBox* pSpinBox = static_cast(pEditor); + CIntProperty* pInt = static_cast(pProp); + pInt->ValueRef(pData) = pSpinBox->value(); + break; + } + + case EPropertyTypeNew::Sound: + { + WIntegralSpinBox* pSpinBox = static_cast(pEditor); + CSoundProperty* pSound = static_cast(pProp); + pSound->ValueRef(pData) = pSpinBox->value(); + break; + } + + case EPropertyTypeNew::Float: + { + WDraggableSpinBox* pSpinBox = static_cast(pEditor); + CFloatProperty* pFloat = static_cast(pProp); + pFloat->ValueRef(pData) = (float) pSpinBox->value(); + break; + } + + case EPropertyTypeNew::Color: + { + WColorPicker* pColorPicker = static_cast(pEditor); + CColorProperty* pColor = static_cast(pProp); + + QColor Color = pColorPicker->Color(); + pColor->ValueRef(pData) = TO_CCOLOR(Color); + break; + } + + case EPropertyTypeNew::String: + { + QLineEdit* pLineEdit = static_cast(pEditor); + CStringProperty* pString = static_cast(pProp); + pString->ValueRef(pData) = TO_TSTRING(pLineEdit->text()); + break; + } + + case EPropertyTypeNew::Enum: + case EPropertyTypeNew::Choice: + { + QComboBox* pComboBox = static_cast(pEditor); + CEnumProperty* pEnum = static_cast(pProp); + pEnum->ValueRef(pData) = pEnum->ValueID(pComboBox->currentIndex()); + break; + } + + case EPropertyTypeNew::Asset: + { + CResourceSelector* pSelector = static_cast(pEditor); + CResourceEntry* pEntry = pSelector->Entry(); + + CAssetProperty* pAsset = static_cast(pProp); + pAsset->ValueRef(pData) = (pEntry ? pEntry->ID() : CAssetID::InvalidID(pAsset->Game())); + break; + } + + } + } + + pCommand->SaveNewData(); } - case eShortProperty: + // Array + else { - WIntegralSpinBox *pSpinBox = static_cast(pEditor); - TShortProperty *pShort = static_cast(pProp); - pShort->Set(pSpinBox->value()); - break; - } - - case eLongProperty: - case eSoundProperty: - { - WIntegralSpinBox *pSpinBox = static_cast(pEditor); - TLongProperty *pLong = static_cast(pProp); - pLong->Set(pSpinBox->value()); - break; - } - - case eFloatProperty: - { - WDraggableSpinBox *pSpinBox = static_cast(pEditor); - TFloatProperty *pFloat = static_cast(pProp); - pFloat->Set((float) pSpinBox->value()); - break; - } - - case eColorProperty: - { - WColorPicker *pColorPicker = static_cast(pEditor); - TColorProperty *pColor = static_cast(pProp); - - QColor Color = pColorPicker->Color(); - pColor->Set(TO_CCOLOR(Color)); - break; - } - - case eStringProperty: - { - QLineEdit *pLineEdit = static_cast(pEditor); - TStringProperty *pString = static_cast(pProp); - pString->Set(TO_TSTRING(pLineEdit->text())); - break; - } - - case eEnumProperty: - { - QComboBox *pComboBox = static_cast(pEditor); - TEnumProperty *pEnum = static_cast(pProp); - CEnumTemplate *pTemp = static_cast(pProp->Template()); - pEnum->Set(pTemp->EnumeratorID(pComboBox->currentIndex())); - break; - } - - case eAssetProperty: - { - CResourceSelector *pSelector = static_cast(pEditor); - CResourceEntry *pEntry = pSelector->Entry(); - - TAssetProperty *pAsset = static_cast(pProp); - pAsset->Set(pEntry ? pEntry->ID() : CAssetID::InvalidID(mpEditor->CurrentGame())); - break; - } - - case eArrayProperty: - { - WIntegralSpinBox *pSpinBox = static_cast(pEditor); - CArrayProperty *pArray = static_cast(pProp); + //FIXME + /* + WIntegralSpinBox* pSpinBox = static_cast(pEditor); + CArrayProperty* pArray = static_cast(pProp); int NewCount = pSpinBox->value(); if (pArray->Count() != NewCount) @@ -452,91 +489,35 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo CResizeScriptArrayCommand *pCmd = new CResizeScriptArrayCommand(pProp, mpEditor, mpModel, NewCount); mpEditor->UndoStack()->push(pCmd); } - break; - } - + */ } } - // Check for character/bitfield/vector/color sub-properties - else if (rkIndex.internalId() & 0x1) - { - pProp = mpModel->PropertyForIndex(rkIndex, true); - - IPropertyValue *pRawValue = pProp->RawValue(); - pOldValue = pRawValue ? pRawValue->Clone() : nullptr; - - if (pProp->Type() == eCharacterProperty) - SetCharacterModelData(pEditor, rkIndex); - - else if (pProp->Type() == eBitfieldProperty) - { - QCheckBox *pCheckBox = static_cast(pEditor); - TBitfieldProperty *pBitfield = static_cast(pProp); - u32 Mask = static_cast(pProp->Template())->FlagMask(rkIndex.row()); - - int Flags = pBitfield->Get(); - if (pCheckBox->isChecked()) Flags |= Mask; - else Flags &= ~Mask; - pBitfield->Set(Flags); - } - - else - { - WDraggableSpinBox *pSpinBox = static_cast(pEditor); - - if (pProp->Type() == eVector3Property) - { - TVector3Property *pVector = static_cast(pProp); - CVector3f Value = pVector->Get(); - - if (rkIndex.row() == 0) Value.X = (float) pSpinBox->value(); - if (rkIndex.row() == 1) Value.Y = (float) pSpinBox->value(); - if (rkIndex.row() == 2) Value.Z = (float) pSpinBox->value(); - - pVector->Set(Value); - } - - else if (pProp->Type() == eColorProperty) - { - TColorProperty *pColor = static_cast(pProp); - CColor Value = pColor->Get(); - - if (rkIndex.row() == 0) Value.R = (float) pSpinBox->value(); - if (rkIndex.row() == 1) Value.G = (float) pSpinBox->value(); - if (rkIndex.row() == 2) Value.B = (float) pSpinBox->value(); - if (rkIndex.row() == 3) Value.A = (float) pSpinBox->value(); - - pColor->Set(Value); - } - } - } - - if (pProp && pOldValue) + if (pCommand) { // Check for edit in progress - bool Matches = pOldValue->Matches(pProp->RawValue()); + bool DataChanged = pCommand->IsNewDataDifferent(); - if (!Matches && mInRelayWidgetEdit && (pEditor->hasFocus() || pProp->Type() == eColorProperty)) + if (DataChanged && mInRelayWidgetEdit && (pEditor->hasFocus() || pProp->Type() == EPropertyTypeNew::Color)) mEditInProgress = true; - bool EditInProgress = mEditInProgress; + bool EditWasInProgress = mEditInProgress; // Check for edit finished - if (!mInRelayWidgetEdit || (!pEditor->hasFocus() && pProp->Type() != eColorProperty)) + if (!mInRelayWidgetEdit || (!pEditor->hasFocus() && pProp->Type() != EPropertyTypeNew::Color)) mEditInProgress = false; - // Create undo command - if (!Matches || EditInProgress) + // Push undo command + if (DataChanged || EditWasInProgress) { // Always consider the edit done for bool properties - CEditScriptPropertyCommand *pCommand = new CEditScriptPropertyCommand(pProp, mpEditor, pOldValue, (!mEditInProgress || pProp->Type() == eBoolProperty)); + pCommand->SetEditComplete(!mEditInProgress || pProp->Type() == EPropertyTypeNew::Bool); mpEditor->UndoStack()->push(pCommand); } else - delete pOldValue; - }*/ + delete pCommand; + } } bool CPropertyDelegate::eventFilter(QObject *pObject, QEvent *pEvent) @@ -558,18 +539,16 @@ bool CPropertyDelegate::eventFilter(QObject *pObject, QEvent *pEvent) // Character properties have separate functions because they're somewhat complicated - they have different layouts in different games QWidget* CPropertyDelegate::CreateCharacterEditor(QWidget *pParent, const QModelIndex& rkIndex) const { - //FIXME -/* TCharacterProperty *pProp = static_cast(mpModel->PropertyForIndex(rkIndex, true)); - CAnimationParameters Params = pProp->Get(); + CAnimationSetProperty* pAnimSetProp = TPropCast(mpModel->PropertyForIndex(rkIndex, true)); + CAnimationParameters Params = pAnimSetProp->Value(mpModel->GetPropertyData()); // Determine property type - EPropertyType Type = DetermineCharacterPropType(Params.Version(), rkIndex); - if (Type == eUnknownProperty) return nullptr; + EPropertyTypeNew Type = DetermineCharacterPropType(Params.Version(), rkIndex); // Create widget - if (Type == eAssetProperty) + if (Type == EPropertyTypeNew::Asset) { - CResourceSelector *pSelector = new CResourceSelector(pParent); + CResourceSelector* pSelector = new CResourceSelector(pParent); pSelector->SetFrameVisible(false); if (Params.Version() <= eEchoes) @@ -581,94 +560,87 @@ QWidget* CPropertyDelegate::CreateCharacterEditor(QWidget *pParent, const QModel return pSelector; } - if (Type == eEnumProperty) + else if (Type == EPropertyTypeNew::Enum || Type == EPropertyTypeNew::Choice) { - QComboBox *pComboBox = new QComboBox(pParent); - - CAnimSet *pAnimSet = Params.AnimSet(); + QComboBox* pComboBox = new QComboBox(pParent); + CAnimSet* pAnimSet = Params.AnimSet(); if (pAnimSet) { - for (u32 iChr = 0; iChr < pAnimSet->NumCharacters(); iChr++) - pComboBox->addItem(TO_QSTRING(pAnimSet->Character(iChr)->Name)); + for (u32 CharIdx = 0; CharIdx < pAnimSet->NumCharacters(); CharIdx++) + pComboBox->addItem(TO_QSTRING(pAnimSet->Character(CharIdx)->Name)); } CONNECT_RELAY(pComboBox, rkIndex, currentIndexChanged(int)); return pComboBox; } - if (Type == eLongProperty) + else if (Type == EPropertyTypeNew::Int) { WIntegralSpinBox *pSpinBox = new WIntegralSpinBox(pParent); CONNECT_RELAY(pSpinBox, rkIndex, valueChanged(int)); return pSpinBox; - }*/ + } return nullptr; } void CPropertyDelegate::SetCharacterEditorData(QWidget *pEditor, const QModelIndex& rkIndex) const { - //FIXME - /* - TCharacterProperty *pProp = static_cast(mpModel->PropertyForIndex(rkIndex, true)); - CAnimationParameters Params = pProp->Get(); - EPropertyType Type = DetermineCharacterPropType(Params.Version(), rkIndex); + CAnimationSetProperty* pAnimSetProp = TPropCast(mpModel->PropertyForIndex(rkIndex, true)); + CAnimationParameters Params = pAnimSetProp->Value(mpModel->GetPropertyData()); + EPropertyTypeNew Type = DetermineCharacterPropType(Params.Version(), rkIndex); - if (Type == eAssetProperty) + if (Type == EPropertyTypeNew::Asset) { static_cast(pEditor)->SetResource(Params.AnimSet()); } - else if (Type == eEnumProperty) + else if (Type == EPropertyTypeNew::Enum || Type == EPropertyTypeNew::Choice) { static_cast(pEditor)->setCurrentIndex(Params.CharacterIndex()); } - else if (Type == eLongProperty && !pEditor->hasFocus()) + else if (Type == EPropertyTypeNew::Int && !pEditor->hasFocus()) { int UnkIndex = (Params.Version() <= eEchoes ? rkIndex.row() - 2 : rkIndex.row() - 1); u32 Value = Params.Unknown(UnkIndex); static_cast(pEditor)->setValue(Value); } - */ } void CPropertyDelegate::SetCharacterModelData(QWidget *pEditor, const QModelIndex& rkIndex) const { - //FIXME - /* - CAnimationSetProperty* pAnimSet = TPropCast(mpModel->PropertyForIndex(rkIndex, true)); - CAnimationParameters Params = pAnimSet->Get(); - EPropertyType Type = DetermineCharacterPropType(Params.Version(), rkIndex); + CAnimationSetProperty* pAnimSetProp = TPropCast(mpModel->PropertyForIndex(rkIndex, true)); + CAnimationParameters Params = pAnimSetProp->Value(mpModel->GetPropertyData()); + EPropertyTypeNew Type = DetermineCharacterPropType(Params.Version(), rkIndex); - if (Type == eAssetProperty) + if (Type == EPropertyTypeNew::Asset) { CResourceEntry *pEntry = static_cast(pEditor)->Entry(); Params.SetResource( pEntry ? pEntry->ID() : CAssetID::InvalidID(mpEditor->CurrentGame()) ); } - else if (Type == eEnumProperty) + else if (Type == EPropertyTypeNew::Enum || Type == EPropertyTypeNew::Choice) { Params.SetCharIndex( static_cast(pEditor)->currentIndex() ); } - else if (Type == eLongProperty) + else if (Type == EPropertyTypeNew::Int) { int UnkIndex = (Params.Version() <= eEchoes ? rkIndex.row() - 2 : rkIndex.row() - 1); Params.SetUnknown(UnkIndex, static_cast(pEditor)->value() ); } - pProp->Set(Params); + pAnimSetProp->ValueRef(mpModel->GetPropertyData()) = Params; // If we just updated the resource, make sure all the sub-properties of the character are flagged as changed. // We want to do this -after- updating the anim params on the property, which is why we have a second type check. - if (Type == eAssetProperty) + if (Type == EPropertyTypeNew::Asset) { QModelIndex ParentIndex = rkIndex.parent(); mpModel->dataChanged(mpModel->index(1, 1, ParentIndex), mpModel->index(mpModel->rowCount(ParentIndex) - 1, 1, ParentIndex)); } - */ } EPropertyTypeNew CPropertyDelegate::DetermineCharacterPropType(EGame Game, const QModelIndex& rkIndex) const diff --git a/src/Editor/PropertyEdit/CPropertyModel.cpp b/src/Editor/PropertyEdit/CPropertyModel.cpp index 021bbb90..0582a00b 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.cpp +++ b/src/Editor/PropertyEdit/CPropertyModel.cpp @@ -18,14 +18,14 @@ CPropertyModel::CPropertyModel(QObject *pParent /*= 0*/) int CPropertyModel::RecursiveBuildArrays(IPropertyNew* pProperty, int ParentID) { + int MyID = mProperties.size(); mProperties << SProperty(); - SProperty& Property = mProperties.back(); - Property.pProperty = pProperty; - Property.ParentID = ParentID; - int MyID = mProperties.size() - 1; + mProperties[MyID].pProperty = pProperty; + mProperties[MyID].ParentID = ParentID; + int RowNumber = (ParentID >= 0 ? mProperties[ParentID].ChildIDs.size() : 0); - Property.Index = createIndex(RowNumber, 0, pProperty); + mProperties[MyID].Index = createIndex(RowNumber, 0, MyID); if (pProperty->Type() == EPropertyTypeNew::Array) { @@ -34,7 +34,7 @@ int CPropertyModel::RecursiveBuildArrays(IPropertyNew* pProperty, int ParentID) for (u32 ElementIdx = 0; ElementIdx < pArray->ArrayCount(mpPropertyData); ElementIdx++) { int NewChildID = RecursiveBuildArrays( pArray->Archetype(), MyID ); - Property.ChildIDs.push_back(NewChildID); + mProperties[MyID].ChildIDs.push_back(NewChildID); } } else @@ -42,7 +42,7 @@ int CPropertyModel::RecursiveBuildArrays(IPropertyNew* pProperty, int ParentID) for (u32 ChildIdx = 0; ChildIdx < pProperty->NumChildren(); ChildIdx++) { int NewChildID = RecursiveBuildArrays( pProperty->ChildByIndex(ChildIdx), MyID ); - Property.ChildIDs.push_back(NewChildID); + mProperties[MyID].ChildIDs.push_back(NewChildID); } } @@ -74,7 +74,7 @@ void CPropertyModel::ConfigureIntrinsic(CGameProject* pProject, IPropertyNew* pR void CPropertyModel::ConfigureScript(CGameProject* pProject, IPropertyNew* pRootProperty, CScriptObject* pObject) { - ConfigureIntrinsic(pProject, pRootProperty, pObject); + ConfigureIntrinsic(pProject, pRootProperty, pObject ? pObject->PropertyData() : nullptr); mpObject = pObject; } @@ -166,7 +166,7 @@ QVariant CPropertyModel::data(const QModelIndex& rkIndex, int Role) const if (Role == Qt::DisplayRole || (Role == Qt::ToolTipRole && rkIndex.column() == 1) ) { - if (rkIndex.internalId() & 0x1) + if (rkIndex.internalId() & 0x80000000) { IPropertyNew *pProp = PropertyForIndex(rkIndex, true); EPropertyTypeNew Type = pProp->Type(); @@ -321,11 +321,6 @@ QVariant CPropertyModel::data(const QModelIndex& rkIndex, int Role) const // No display text on properties with persistent editors case EPropertyTypeNew::Bool: - if (Role == Qt::DisplayRole) - return TPropCast(pProp)->Value(mpPropertyData) ? "True" : "False"; - else - return ""; - case EPropertyTypeNew::Asset: case EPropertyTypeNew::Color: if (Role == Qt::DisplayRole) @@ -341,12 +336,12 @@ QVariant CPropertyModel::data(const QModelIndex& rkIndex, int Role) const if (Role == Qt::ToolTipRole && rkIndex.column() == 0) { - if (!(rkIndex.internalId() & 0x1)) + if (!(rkIndex.internalId() & 0x80000000)) { // Add name IPropertyNew *pProp = PropertyForIndex(rkIndex, false); QString DisplayText = data(rkIndex, Qt::DisplayRole).toString(); - QString Text = QString("%1 (%2)").arg(DisplayText).arg(TO_QSTRING(PropEnumToPropString(pProp->Type()))); + QString Text = QString("%1 (%2)").arg(DisplayText).arg(pProp->HashableTypeName()); // Add uncooked notification if (pProp->CookPreference() == ECookPreferenceNew::Never) @@ -444,7 +439,10 @@ QModelIndex CPropertyModel::parent(const QModelIndex& rkChild) const else ID = mProperties[ID].ParentID; - return mProperties[ID].Index; + if (ID >= 0) + return mProperties[ID].Index; + else + return QModelIndex(); } Qt::ItemFlags CPropertyModel::flags(const QModelIndex& rkIndex) const diff --git a/src/Editor/PropertyEdit/CPropertyView.cpp b/src/Editor/PropertyEdit/CPropertyView.cpp index c2869c27..af9a5408 100644 --- a/src/Editor/PropertyEdit/CPropertyView.cpp +++ b/src/Editor/PropertyEdit/CPropertyView.cpp @@ -81,7 +81,7 @@ void CPropertyView::SetEditor(CWorldEditor *pEditor) { mpEditor = pEditor; mpDelegate->SetEditor(pEditor); - connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IProperty*)), mpModel, SLOT(NotifyPropertyModified(CScriptObject*,IProperty*))); + connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IPropertyNew*)), mpModel, SLOT(NotifyPropertyModified(CScriptObject*,IPropertyNew*))); } void CPropertyView::SetInstance(CScriptObject *pObj) diff --git a/src/Editor/ResourceBrowser/CResourceBrowser.cpp b/src/Editor/ResourceBrowser/CResourceBrowser.cpp index 94de4c23..57b85a97 100644 --- a/src/Editor/ResourceBrowser/CResourceBrowser.cpp +++ b/src/Editor/ResourceBrowser/CResourceBrowser.cpp @@ -63,6 +63,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent) mpProxyModel = new CResourceProxyModel(this); mpProxyModel->setSourceModel(mpModel); mpUI->ResourceTableView->setModel(mpProxyModel); + mpUI->ResourceTableView->resizeRowsToContents(); QHeaderView *pHeader = mpUI->ResourceTableView->horizontalHeader(); pHeader->setSectionResizeMode(0, QHeaderView::Stretch); @@ -158,6 +159,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent) connect(mpUI->ResourceTableView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnResourceSelectionChanged(QModelIndex))); connect(mpProxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)), mpUI->ResourceTableView, SLOT(resizeRowsToContents())); connect(mpProxyModel, SIGNAL(layoutChanged(QList,QAbstractItemModel::LayoutChangeHint)), mpUI->ResourceTableView, SLOT(resizeRowsToContents())); + connect(mpProxyModel, SIGNAL(modelReset()), mpUI->ResourceTableView, SLOT(resizeRowsToContents())); connect(mpFilterAllBox, SIGNAL(toggled(bool)), this, SLOT(OnFilterTypeBoxTicked(bool))); connect(gpEdApp, SIGNAL(ActiveProjectChanged(CGameProject*)), this, SLOT(UpdateStore())); } @@ -874,6 +876,9 @@ void CResourceBrowser::UpdateFilter() UpdateDescriptionLabel(); mpProxyModel->SetSearchString( TO_TSTRING(mpUI->SearchBar->text()) ); mpProxyModel->invalidate(); + + // not sure why I need to do this here? but the resize mode seems to get reset otherwise + mpUI->ResourceTableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); } void CResourceBrowser::UpdateUndoActionStates() diff --git a/src/Editor/Undo/CEditScriptPropertyCommand.h b/src/Editor/Undo/CEditScriptPropertyCommand.h index 8cf3ea19..8b8fc909 100644 --- a/src/Editor/Undo/CEditScriptPropertyCommand.h +++ b/src/Editor/Undo/CEditScriptPropertyCommand.h @@ -7,29 +7,83 @@ #include "Editor/PropertyEdit/CPropertyModel.h" #include "Editor/WorldEditor/CWorldEditor.h" -template -class TEditScriptPropertyCommand : public IUndoCommand +class IEditPropertyCommand : public IUndoCommand { - typedef typename PropertyClass::ValueType ValueType; + std::vector mOldData; + std::vector mNewData; - CWorldEditor *mpEditor; - CInstancePtr mpInstance; - PropertyClass* mpProperty; - ValueType mOldValue; - ValueType mNewValue; +protected: + IPropertyNew* mpProperty; bool mCommandEnded; + bool mSavedOldData; + bool mSavedNewData; + + /** Save the current state of the object properties to the given data buffer */ + void SaveObjectStateToArray(std::vector& rVector) + { + CVectorOutStream MemStream(&rVector, IOUtil::kSystemEndianness); + CBasicBinaryWriter Writer(&MemStream, CSerialVersion(IArchive::skCurrentArchiveVersion, 0, mpProperty->Game())); + + std::vector DataPointers; + GetObjectDataPointers(DataPointers); + + for (int PtrIdx = 0; PtrIdx < DataPointers.size(); PtrIdx++) + { + void* pData = DataPointers[PtrIdx]; + mpProperty->SerializeValue(pData, Writer); + } + } + + /** Restore the state of the object properties from the given data buffer */ + void RestoreObjectStateFromArray(std::vector& rArray) + { + CBasicBinaryReader Reader(rArray.data(), rArray.size(), CSerialVersion(IArchive::skCurrentArchiveVersion, 0, mpProperty->Game())); + + std::vector DataPointers; + GetObjectDataPointers(DataPointers); + + for (int PtrIdx = 0; PtrIdx < DataPointers.size(); PtrIdx++) + { + void* pData = DataPointers[PtrIdx]; + mpProperty->SerializeValue(pData, Reader); + } + } public: - TEditScriptPropertyCommand(PropertyClass* pProp, CScriptObject* pInstance, CWorldEditor* pEditor, ValueType NewValue, bool IsDone, const QString& rkCommandName = "Edit Property") + IEditPropertyCommand(IPropertyNew* pProperty, const QString& rkCommandName = "Edit Property") : IUndoCommand(rkCommandName) - , mpEditor(pEditor) - , mpInstance(pInstance) - , mpProperty(pProp) - , mOldValue(pProp->Value(pInstance->PropertyData())) - , mNewValue(NewValue) - , mCommandEnded(IsDone) + , mpProperty(pProperty) + , mSavedOldData(false) + , mSavedNewData(false) {} + void SaveOldData() + { + SaveObjectStateToArray(mOldData); + mSavedOldData = true; + } + + void SaveNewData() + { + SaveObjectStateToArray(mNewData); + mSavedNewData = true; + } + + bool IsNewDataDifferent() + { + if (mOldData.size() != mNewData.size()) return false; + return memcmp(mOldData.data(), mNewData.data(), mNewData.size()) != 0; + } + + void SetEditComplete(bool IsComplete) + { + mCommandEnded = IsComplete; + } + + /** Interface */ + virtual void GetObjectDataPointers(std::vector& rOutPointers) const = 0; + + /** IUndoCommand/QUndoCommand interface */ int id() const { return eEditScriptPropertyCmd; @@ -39,13 +93,29 @@ public: { if (!mCommandEnded) { - TEditScriptPropertyCommand* pkCmd = dynamic_cast(pkOther); + const IEditPropertyCommand* pkCmd = dynamic_cast(pkOther); - if (pkCmd && pkCmd->mpProperty == mpProperty && pkCmd->mpInstance == mpInstance) + if (pkCmd && pkCmd->mpProperty == mpProperty) { - mNewValue = pkCmd->mNewValue; - mCommandEnded = pkCmd->mCommandEnded; - return true; + std::vector MyPointers; + GetObjectDataPointers(MyPointers); + + std::vector TheirPointers; + pkCmd->GetObjectDataPointers(TheirPointers); + + if (TheirPointers.size() == MyPointers.size()) + { + for (int PtrIdx = 0; PtrIdx < MyPointers.size(); PtrIdx++) + { + if (MyPointers[PtrIdx] != TheirPointers[PtrIdx]) + return false; + } + + // Match + mNewData = pkCmd->mNewData; + mCommandEnded = pkCmd->mCommandEnded; + return true; + } } } @@ -54,17 +124,15 @@ public: void undo() { - void* pData = mpInstance->PropertyData(); - mpProperty->ValueRef(pData) = mOldValue; - mpEditor->OnPropertyModified(mpProperty); + ASSERT(mSavedOldData && mSavedNewData); + RestoreObjectStateFromArray(mOldData); mCommandEnded = true; } void redo() { - void* pData = mpInstance->PropertyData(); - mpProperty->ValueRef(pData) = mNewValue; - mpEditor->OnPropertyModified(mpProperty); + ASSERT(mSavedOldData && mSavedNewData); + RestoreObjectStateFromArray(mNewData); } bool AffectsCleanState() const @@ -73,22 +141,46 @@ public: } }; -/*class CEditScriptPropertyCommand : public IUndoCommand +class CEditScriptPropertyCommand : public IEditPropertyCommand { - CWorldEditor *mpEditor; - CPropertyPtr mpProp; - IPropertyValue *mpOldValue; - IPropertyValue *mpNewValue; - bool mCommandEnded; + std::vector mInstances; + CWorldEditor* mpEditor; public: - CEditScriptPropertyCommand(IProperty *pProp, CWorldEditor *pEditor, IPropertyValue *pOldValue, bool IsDone, const QString& rkCommandName = "Edit Property"); - ~CEditScriptPropertyCommand(); - int id() const; - bool mergeWith(const QUndoCommand *pkOther); - void undo(); - void redo(); - bool AffectsCleanState() const { return true; } -};*/ + CEditScriptPropertyCommand(CWorldEditor* pEditor, CScriptObject* pInstance, IPropertyNew* pProperty, const QString& rkCommandName = "Edit Property") + : IEditPropertyCommand(pProperty, rkCommandName) + , mpEditor(pEditor) + { + mInstances.push_back( CInstancePtr(pInstance) ); + } + + virtual void GetObjectDataPointers(std::vector& rOutPointers) const override + { + rOutPointers.resize(mInstances.size()); + + for (int InstanceIdx = 0; InstanceIdx < mInstances.size(); InstanceIdx++) + { + rOutPointers[InstanceIdx] = mInstances[InstanceIdx]->PropertyData(); + } + } + + virtual void undo() override + { + IEditPropertyCommand::undo(); + NotifyWorldEditor(); + } + + virtual void redo() override + { + IEditPropertyCommand::redo(); + NotifyWorldEditor(); + } + + void NotifyWorldEditor() + { + for (int InstanceIdx = 0; InstanceIdx < mInstances.size(); InstanceIdx++) + mpEditor->OnPropertyModified(*mInstances[InstanceIdx], mpProperty); + } +}; #endif // CEDITSCRIPTPROPERTYCOMMAND_H diff --git a/src/Editor/Widgets/CSoftValidatorLineEdit.h b/src/Editor/Widgets/CSoftValidatorLineEdit.h index dda4a50c..e2b476f3 100644 --- a/src/Editor/Widgets/CSoftValidatorLineEdit.h +++ b/src/Editor/Widgets/CSoftValidatorLineEdit.h @@ -34,8 +34,10 @@ protected slots: if ( mpSoftValidator ) { + QString Text = text(); int DummyPos; - if ( mpSoftValidator->validate(text(), DummyPos) == QValidator::Acceptable ) + + if ( mpSoftValidator->validate(Text, DummyPos) == QValidator::Acceptable ) { NewValidity = true; setStyleSheet("border: 1px solid green"); diff --git a/src/Editor/WorldEditor/CWorldEditor.cpp b/src/Editor/WorldEditor/CWorldEditor.cpp index f77925ca..c3e8ee7a 100644 --- a/src/Editor/WorldEditor/CWorldEditor.cpp +++ b/src/Editor/WorldEditor/CWorldEditor.cpp @@ -569,11 +569,12 @@ void CWorldEditor::OnLinksModified(const QList& rkInstances) emit InstanceLinksModified(rkInstances); } -void CWorldEditor::OnPropertyModified(IPropertyNew *pProp) +void CWorldEditor::OnPropertyModified(CScriptObject* pObject, IPropertyNew *pProp) { - if (!mpSelection->IsEmpty() && mpSelection->Front()->NodeType() == eScriptNode) + CScriptNode *pScript = mScene.NodeForInstance(pObject); + + if (pScript) { - CScriptNode *pScript = static_cast(mpSelection->Front()); pScript->PropertyModified(pProp); // If this is an editor property, update other parts of the UI to reflect the new value. @@ -581,23 +582,24 @@ void CWorldEditor::OnPropertyModified(IPropertyNew *pProp) { UpdateStatusBar(); UpdateSelectionUI(); + mpSelection->UpdateBounds(); } - - // If this is a model/character, then we'll treat this as a modified selection. This is to make sure the selection bounds updates. - if (pProp->Type() == EPropertyTypeNew::Asset) - { - CAssetProperty *pAsset = TPropCast(pProp); - const CResTypeFilter& rkFilter = pAsset->GetTypeFilter(); - - if (rkFilter.Accepts(eModel) || rkFilter.Accepts(eAnimSet) || rkFilter.Accepts(eCharacter)) - SelectionModified(); - } - else if (pProp->Type() == EPropertyTypeNew::AnimationSet) - SelectionModified(); - - // Emit signal so other widgets can react to the property change - emit PropertyModified(pScript->Instance(), pProp); } + + // If this is a model/character, then we'll treat this as a modified selection. This is to make sure the selection bounds updates. + if (pProp->Type() == EPropertyTypeNew::Asset) + { + CAssetProperty *pAsset = TPropCast(pProp); + const CResTypeFilter& rkFilter = pAsset->GetTypeFilter(); + + if (rkFilter.Accepts(eModel) || rkFilter.Accepts(eAnimSet) || rkFilter.Accepts(eCharacter)) + SelectionModified(); + } + else if (pProp->Type() == EPropertyTypeNew::AnimationSet) + SelectionModified(); + + // Emit signal so other widgets can react to the property change + emit PropertyModified(pObject, pProp); } void CWorldEditor::SetSelectionActive(bool Active) diff --git a/src/Editor/WorldEditor/CWorldEditor.h b/src/Editor/WorldEditor/CWorldEditor.h index 766bd9c0..201e4970 100644 --- a/src/Editor/WorldEditor/CWorldEditor.h +++ b/src/Editor/WorldEditor/CWorldEditor.h @@ -122,7 +122,7 @@ public slots: void OnActiveProjectChanged(CGameProject *pProj); void OnLinksModified(const QList& rkInstances); - void OnPropertyModified(IPropertyNew *pProp); + void OnPropertyModified(CScriptObject* pObject, IPropertyNew *pProp); void SetSelectionActive(bool Active); void SetSelectionInstanceNames(const QString& rkNewName, bool IsDone); void SetSelectionLayer(CScriptLayer *pLayer); diff --git a/src/Editor/WorldEditor/WEditorProperties.cpp b/src/Editor/WorldEditor/WEditorProperties.cpp index 3a416d66..e3c2e3bc 100644 --- a/src/Editor/WorldEditor/WEditorProperties.cpp +++ b/src/Editor/WorldEditor/WEditorProperties.cpp @@ -65,7 +65,7 @@ void WEditorProperties::SyncToEditor(CWorldEditor *pEditor) connect(mpEditor, SIGNAL(SelectionModified()), this, SLOT(OnSelectionModified())); connect(mpEditor, SIGNAL(LayersModified()), this, SLOT(OnLayersModified())); connect(mpEditor, SIGNAL(InstancesLayerChanged(QList)), this, SLOT(OnInstancesLayerChanged(QList))); - connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IProperty*)), this, SLOT(OnPropertyModified(CScriptObject*,IProperty*))); + connect(mpEditor, SIGNAL(PropertyModified(CScriptObject*,IPropertyNew*)), this, SLOT(OnPropertyModified(CScriptObject*,IPropertyNew*))); OnLayersModified(); } diff --git a/src/Editor/main.cpp b/src/Editor/main.cpp index 12d83bcb..2ae5e8c2 100644 --- a/src/Editor/main.cpp +++ b/src/Editor/main.cpp @@ -73,7 +73,7 @@ int main(int argc, char *argv[]) // Load templates CTemplateLoader::LoadGameList(); - CTemplateLoader::LoadAllGames(); + //CTemplateLoader::LoadAllGames(); //CTemplateWriter::SaveAllTemplates(); // Execute application diff --git a/src/Math/CMatrix4f.cpp b/src/Math/CMatrix4f.cpp index a819764d..9a3b2ac8 100644 --- a/src/Math/CMatrix4f.cpp +++ b/src/Math/CMatrix4f.cpp @@ -139,16 +139,6 @@ float CMatrix4f::Determinant() const } // ************ OPERATORS ************ -inline float* CMatrix4f::operator[](long Index) -{ - return m[Index]; -} - -inline const float* CMatrix4f::operator[](long Index) const -{ - return m[Index]; -} - CVector3f CMatrix4f::operator*(const CVector3f& rkVec) const { // For vec3 multiplication, the vector w component is considered to be 1.0 diff --git a/src/Math/CMatrix4f.h b/src/Math/CMatrix4f.h index e31220a4..703df8f5 100644 --- a/src/Math/CMatrix4f.h +++ b/src/Math/CMatrix4f.h @@ -42,4 +42,14 @@ public: static const CMatrix4f skIdentity; }; +inline float* CMatrix4f::operator[](long Index) +{ + return m[Index]; +} + +inline const float* CMatrix4f::operator[](long Index) const +{ + return m[Index]; +} + #endif // CMATRIX4_H diff --git a/src/Math/CVector3f.cpp b/src/Math/CVector3f.cpp index af81c46e..1b8a771d 100644 --- a/src/Math/CVector3f.cpp +++ b/src/Math/CVector3f.cpp @@ -43,7 +43,7 @@ void CVector3f::Serialize(IArchive& rArc) TString CVector3f::ToString() const { - return TString::Format("%f.1, %f.1, %f.1", X, Y, Z); + return TString::Format("%.1f, %.1f, %.1f", X, Y, Z); } // ************ SWIZZLE ************