Files
UnrealEngineUWP/Engine/Source/Editor/StructUtilsEditor/Private/StructUtilsEditorModule.cpp
yoan stamant dacfa4f383 Moved StructUtils types (InstancedStruct, StructView, PropertyBag, etc.) to CoreUObject
StructUtilsTestSuite has been moved to Developper
StructUtilsEditor has been moved to Engine/Editor
NetSerialization for FInstancedStruct not using native serialization has been moved to the engine module since FRepLayout is not available in CoreUObject module.
Old plugins and modules are kept temporarily and will be remove in a different CL
Temporary header files added to facilitate transition to the new include path
#jira UE-216472
#rb Devin.Doucette, Francis.Hurteau

[CL 34509881 by yoan stamant in ue5-main branch]
2024-06-19 15:22:25 -04:00

188 lines
7.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "StructUtilsEditorModule.h"
#include "CoreGlobals.h"
#include "GameFramework/Actor.h"
#include "Modules/ModuleManager.h"
#include "PropertyBagDetails.h"
#include "PropertyEditorModule.h"
#include "Serialization/PropertyLocalizationDataGathering.h"
#include "StructUtils/InstancedStruct.h"
#include "StructUtils/InstancedStructContainer.h"
#include "StructUtils/UserDefinedStruct.h"
#include "StructUtilsDelegates.h"
#include "UObject/Package.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/UObjectIterator.h"
#include "UserDefinedStructure/UserDefinedStructEditorData.h"
#define LOCTEXT_NAMESPACE "StructUtilsEditor"
IMPLEMENT_MODULE(FStructUtilsEditorModule, StructUtilsEditor)
namespace UE::StructUtils::Private
{
void GatherForLocalization(const FString& PathToParent, const UScriptStruct* Struct, const void* StructData, const void* DefaultStructData,
FPropertyLocalizationDataGatherer& PropertyLocalizationDataGatherer, const EPropertyLocalizationGathererTextFlags GatherTextFlags)
{
const FInstancedStruct* ThisInstance = static_cast<const FInstancedStruct*>(StructData);
const FInstancedStruct* DefaultInstance = static_cast<const FInstancedStruct*>(DefaultStructData);
PropertyLocalizationDataGatherer.GatherLocalizationDataFromStruct(PathToParent, Struct, StructData, DefaultStructData, GatherTextFlags);
if (const UScriptStruct* StructTypePtr = ThisInstance->GetScriptStruct())
{
const uint8* DefaultInstanceMemory = nullptr;
if (DefaultInstance)
{
// Types must match
if (StructTypePtr == DefaultInstance->GetScriptStruct())
{
DefaultInstanceMemory = DefaultInstance->GetMemory();
}
}
PropertyLocalizationDataGatherer.GatherLocalizationDataFromStructWithCallbacks(PathToParent + TEXT(".StructInstance"), StructTypePtr, ThisInstance->GetMemory(),
DefaultInstanceMemory, GatherTextFlags);
}
}
} // UE::StructUtils::Private
void FStructUtilsEditorModule::StartupModule()
{
// Register the details customizer
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomPropertyTypeLayout("InstancedStruct", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FInstancedStructDetails::MakeInstance));
PropertyModule.RegisterCustomPropertyTypeLayout("InstancedPropertyBag", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FPropertyBagDetails::MakeInstance));
PropertyModule.NotifyCustomizationModuleChanged();
// Register for localization
{
static const FAutoRegisterLocalizationDataGatheringCallback AutomaticRegistrationOfLocalizationGatherer(
TBaseStructure<FInstancedStruct>::Get(),
&UE::StructUtils::Private::GatherForLocalization);
}
}
void FStructUtilsEditorModule::ShutdownModule()
{
// Unregister the details customization
if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
{
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.UnregisterCustomPropertyTypeLayout("InstancedStruct");
PropertyModule.UnregisterCustomPropertyTypeLayout("InstancedPropertyBag");
PropertyModule.NotifyCustomizationModuleChanged();
}
}
namespace UE::StructUtils::Private
{
static void VisitReferencedObjects(const UUserDefinedStruct* StructToReinstantiate)
{
// Helper preference collector, does not collect anything, but makes sure AddStructReferencedObjects() gets called e.g. on instanced struct.
class FVisitorReferenceCollector : public FReferenceCollector
{
public:
virtual bool IsIgnoringArchetypeRef() const override { return false; }
virtual bool IsIgnoringTransient() const override { return false; }
virtual void HandleObjectReference(UObject*& Object, const UObject* ReferencingObject, const FProperty* ReferencingProperty) override
{
// Empty
}
};
FVisitorReferenceCollector Collector;
// This sets global variable which read in the AddStructReferencedObjects().
FStructureToReinstantiateScope StructureToReinstantiateScope(StructToReinstantiate);
for (TObjectIterator<UObject> ObjectIt; ObjectIt; ++ObjectIt)
{
UObject* Object = *ObjectIt;
// This sets global variable which read in the AddStructReferencedObjects().
FCurrentReinstantiationOuterObjectScope CurrentReinstantiateOuterObjectScope(Object);
Collector.AddPropertyReferencesWithStructARO(Object->GetClass(), Object);
}
// Handle CDOs and sparse class data
for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
{
UClass* Class = *ClassIt;
// Handle Sparse class data
if (void* SparseData = const_cast<void*>(Class->GetSparseClassData(EGetSparseClassDataMethod::ReturnIfNull)))
{
FCurrentReinstantiationOuterObjectScope CurrentReinstantiateOuterObjectScope(Class);
const UScriptStruct* SparseDataStruct = Class->GetSparseClassDataStruct();
Collector.AddPropertyReferencesWithStructARO(SparseDataStruct, SparseData);
}
// Handle CDO
if (UObject* CDO = Class->GetDefaultObject())
{
FCurrentReinstantiationOuterObjectScope CurrentReinstantiateOuterObjectScope(CDO);
Collector.AddPropertyReferencesWithStructARO(Class, CDO);
}
}
}
} // UE::StructUtils::Private
void FStructUtilsEditorModule::PreChange(const UUserDefinedStruct* StructToReinstantiate, FStructureEditorUtils::EStructureEditorChangeInfo Info)
{
if (!StructToReinstantiate)
{
return;
}
// Make a duplicate of the existing struct, and point all instances of the struct to point to the duplicate.
// This is done because the original struct will be changed.
UUserDefinedStruct* DuplicatedStruct = nullptr;
{
const FString ReinstantiatedName = FString::Printf(TEXT("STRUCT_REINST_%s"), *StructToReinstantiate->GetName());
const FName UniqueName = MakeUniqueObjectName(GetTransientPackage(), UUserDefinedStruct::StaticClass(), FName(*ReinstantiatedName));
TGuardValue<FIsDuplicatingClassForReinstancing, bool> IsDuplicatingClassForReinstancing(GIsDuplicatingClassForReinstancing, true);
DuplicatedStruct = (UUserDefinedStruct*)StaticDuplicateObject(StructToReinstantiate, GetTransientPackage(), UniqueName, ~RF_Transactional);
DuplicatedStruct->Guid = StructToReinstantiate->Guid;
DuplicatedStruct->Bind();
DuplicatedStruct->StaticLink(true);
DuplicatedStruct->PrimaryStruct = const_cast<UUserDefinedStruct*>(StructToReinstantiate);
DuplicatedStruct->Status = EUserDefinedStructureStatus::UDSS_Duplicate;
DuplicatedStruct->SetFlags(RF_Transient);
DuplicatedStruct->AddToRoot();
}
UUserDefinedStructEditorData* DuplicatedEditorData = CastChecked<UUserDefinedStructEditorData>(DuplicatedStruct->EditorData);
DuplicatedEditorData->RecreateDefaultInstance();
UE::StructUtils::Private::VisitReferencedObjects(DuplicatedStruct);
DuplicatedStruct->RemoveFromRoot();
}
void FStructUtilsEditorModule::PostChange(const UUserDefinedStruct* StructToReinstantiate, FStructureEditorUtils::EStructureEditorChangeInfo Info)
{
if (!StructToReinstantiate)
{
return;
}
UE::StructUtils::Private::VisitReferencedObjects(StructToReinstantiate);
if (UE::StructUtils::Delegates::OnUserDefinedStructReinstanced.IsBound())
{
UE::StructUtils::Delegates::OnUserDefinedStructReinstanced.Broadcast(*StructToReinstantiate);
}
}
#undef LOCTEXT_NAMESPACE