You can change the way the Unreal editor displays structs or other data types by registering a IPropertyTypeCustomization with the FPropertyEditorModule. This allows you to use Slate to render an entirely custom property editor.
This can be used to improve the usability of data types which might not have a very good default representation otherwise.
Creating a property type customization class
Property type customizations should be placed into an Editor Module. They are not something that should be packaged or used outside of the editor.
The basic class skeleton
The below example shows a skeleton for a property customization class:
//.h
class MY_API FMyCustomization : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance();
virtual void CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override;
};
//.cpp
TSharedRef<IPropertyTypeCustomization> FMyCustomization::MakeInstance()
{
return MakeShared<FMyCustomization>();
}
void FMyCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
HeaderRow
.NameContent()
[
//Create Slate widgets to display in the Name column of the
//property editor here - a good default is to call the
//CreatePropertyNameWidget like below, as it generates
//the standard name column for the property.
PropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
//Create Slate widgets to display the value of the property here.
//What you need to put here depends entirely on what you are doing.
SNew(STextBlock).Text(INVTEXT("Hello"))
];
}
void FMyCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
}
Reading or changing the property value within the property customizer
The PropertyHandle
value passed to the CustomizeHeader
function can be used to access the value of the property being customized.
For example, if we were using this to customize the following struct…
USTRUCT()
struct FMyStruct
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
FName IconName;
};
We can use the following code to read the IconName
property’s value:
TSharedPtr<IPropertyHandle> IconNameProp = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMyStruct, IconName));
FName IconName;
IconNameProp->GetValue(IconName);
The GetValue
function can read various different types of data from the property handle, its behavior depends on the type of variable you pass as a parameter. If you wanted to instead read a float
, you would pass a float
as its parameter instead.
Once you’ve done some edits using the property customizer, you can write the data back in a similar fashion:
TSharedPtr<IPropertyHandle> IconNameProp = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMyStruct, IconName));
IconNameProp->SetValue(FName("Amazing new FName"));
Everything else is done using Slate widgets.
Registering the property customizer
Typically you would register the customizer in the StartupModule
function of an Editor Module:
FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomPropertyTypeLayout(
FMyStruct::StaticStruct()->GetFName(),
FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMyCustomization::MakeInstance)
);
This would register the customizer for the struct type FMyStruct
.
To unregister the customizer, typically in ShutdownModule
:
FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.UnregisterCustomPropertyTypeLayout(FMyStruct::StaticStruct()->GetFName());