Files
UnrealEngine/Engine/Source/Editor/DetailCustomizations/Private/SoftClassPathCustomization.cpp
2025-05-18 13:04:45 +08:00

94 lines
3.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SoftClassPathCustomization.h"
#include "DetailWidgetRow.h"
#include "EditorClassUtils.h"
#include "HAL/PlatformCrt.h"
#include "PropertyCustomizationHelpers.h"
#include "PropertyEditorModule.h"
#include "PropertyHandle.h"
#include "UObject/Class.h"
#include "UObject/Object.h"
#include "UObject/ObjectMacros.h"
#include "UObject/UnrealType.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
class IDetailChildrenBuilder;
void FSoftClassPathCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
PropertyHandle = InPropertyHandle;
const FString& MetaClassName = PropertyHandle->GetMetaData("MetaClass");
const FString& RequiredInterfaceName = PropertyHandle->GetMetaData("RequiredInterface"); // This was the old name, switch to MustImplement to synchronize with class property
const FString& MustImplementName = PropertyHandle->GetMetaData("MustImplement");
TArray<const UClass*> AllowedClasses = PropertyCustomizationHelpers::GetClassesFromMetadataString(PropertyHandle->GetMetaData("AllowedClasses"));
TArray<const UClass*> DisallowedClasses = PropertyCustomizationHelpers::GetClassesFromMetadataString(PropertyHandle->GetMetaData("DisallowedClasses"));
const bool bAllowAbstract = PropertyHandle->HasMetaData("AllowAbstract");
const bool bIsBlueprintBaseOnly = PropertyHandle->HasMetaData("IsBlueprintBaseOnly") || PropertyHandle->HasMetaData("BlueprintBaseOnly");
const bool bAllowNone = !(PropertyHandle->GetMetaDataProperty()->PropertyFlags & CPF_NoClear);
const bool bShowTreeView = PropertyHandle->HasMetaData("ShowTreeView");
const bool bHideViewOptions = PropertyHandle->HasMetaData("HideViewOptions");
const bool bShowDisplayNames = PropertyHandle->HasMetaData("ShowDisplayNames");
const UClass* const MetaClass = !MetaClassName.IsEmpty()
? FEditorClassUtils::GetClassFromString(MetaClassName)
: UObject::StaticClass();
const UClass* const RequiredInterface = !RequiredInterfaceName.IsEmpty()
? FEditorClassUtils::GetClassFromString(RequiredInterfaceName)
: FEditorClassUtils::GetClassFromString(MustImplementName);
HeaderRow
.NameContent()
[
InPropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
.MinDesiredWidth(250.0f)
.MaxDesiredWidth(0.0f)
[
// Add a class entry box. Even though this isn't an class entry, we will simulate one
SNew(SClassPropertyEntryBox)
.MetaClass(MetaClass)
.RequiredInterface(RequiredInterface)
.AllowedClasses(MoveTemp(AllowedClasses))
.DisallowedClasses(MoveTemp(DisallowedClasses))
.AllowAbstract(bAllowAbstract)
.IsBlueprintBaseOnly(bIsBlueprintBaseOnly)
.AllowNone(bAllowNone)
.ShowTreeView(bShowTreeView)
.HideViewOptions(bHideViewOptions)
.ShowDisplayNames(bShowDisplayNames)
.SelectedClass(this, &FSoftClassPathCustomization::OnGetClass)
.OnSetClass(this, &FSoftClassPathCustomization::OnSetClass)
];
}
void FSoftClassPathCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
}
const UClass* FSoftClassPathCustomization::OnGetClass() const
{
FString ClassName;
PropertyHandle->GetValueAsFormattedString(ClassName);
// Do we have a valid cached class pointer?
const UClass* Class = CachedClassPtr.Get();
if(!Class || Class->GetPathName() != ClassName)
{
Class = FEditorClassUtils::GetClassFromString(ClassName);
CachedClassPtr = MakeWeakObjectPtr(const_cast<UClass*>(Class));
}
return Class;
}
void FSoftClassPathCustomization::OnSetClass(const UClass* NewClass)
{
if (PropertyHandle->SetValueFromFormattedString((NewClass) ? NewClass->GetPathName() : "None") == FPropertyAccess::Result::Success)
{
CachedClassPtr = MakeWeakObjectPtr(const_cast<UClass*>(NewClass));
}
}