Download

How to add custom meta data for Properties/functions etc.

So I had this idea about adding custom meta data to certain properties, to make easier to filter them out for possible editor customizations/extensions.

Is it possible to do so, outside of Engine modules, without touching UHT/UBT (or at least not touching their code directly ?).

And if so, could anyone point me in right direction ?

I’ve actually done this a while ago when trying to get arrays to display using enum names instead of indices. (I was later told this can just be done with a static array using the enum MAX value, but the process is still valid.)

Adding and handling metadata is pretty easy, the hard part is registering an editor customization that covers the scope you need. FPropertyEditorModule allows registering customizations either for a whole class, or for a specific property type:


virtual void RegisterCustomClassLayout( FName ClassName, FOnGetDetailCustomizationInstance DetailLayoutDelegate );
virtual void RegisterCustomPropertyTypeLayout( FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate, TSharedPtr<IPropertyTypeIdentifier> Identifier = nullptr, TSharedPtr<IDetailsView> ForSpecificInstance = nullptr );

Now here’s the clincher: You cannot register multiple customizations on the same class or property type. So if you’re looking to customize your own stuff, you should have smooth sailing. But if you’re looking to customize built-in types that already have editor customization (such as TArray, in my case), you’re going to have a bad time.

You can find lots of examples of registering editor customizations across the code, just search for one of the above functions for inspiration. The jist of it is, you’ll need to create an Editor-specific module if you haven’t already (as this is editor-specific code that will not compile in a game build) and do registration in its StartupModule.

For the actual metadata, I’ll just go ahead and share the EnumArrayDetails customization that I had going. (I made it work by customizing UObject – it doesn’t have editor customizations! – but don’t try this at home. ;)) Like I said earlier, this particular customization isn’t necessary after all but it should help you get started.



#include "MyEditorCustomizationPrivatePCH.h"

#include "EnumArrayDetails.h"

#include "ObjectEditorUtils.h"

TSharedRef<IDetailCustomization> FEnumArrayDetails::MakeInstance()
{
	return MakeShareable( new FEnumArrayDetails );
}

void FEnumArrayDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{
	TArray< TWeakObjectPtr<UObject> > CustomObjects;
	DetailBuilder.GetObjectsBeingCustomized( CustomObjects );

	for( auto CustomObject : CustomObjects )
	{
		if( CustomObject.IsValid() )
		{
			UClass* Class = CustomObject->GetClass();

			for( TFieldIterator<UArrayProperty> PropIt( Class ); PropIt; ++PropIt )
			{
				**UArrayProperty* Property = *PropIt;

				if( !Property->HasMetaData( "EnumArray" ) )
				{
					continue;
				}

				const FName EnumName = FName( *Property->GetMetaData( "EnumArray" ) );**
				UEnum* EnumClass = (UEnum*)StaticFindObjectFast( UEnum::StaticClass(), nullptr, EnumName, true, true );

				if( EnumClass == nullptr )
				{
					continue;
				}

				UClass* PropertyClass = Property->GetOwnerClass();
				TSharedRef<IPropertyHandle> Handle = DetailBuilder.GetProperty( Property->GetFName(), PropertyClass );

				if( !Handle->IsValidHandle() )
				{
					continue;
				}

				FName CategoryName = FObjectEditorUtils::GetCategoryFName( Property );
				IDetailCategoryBuilder& Category = DetailBuilder.EditCategory( CategoryName );

				TSharedRef<FDetailArrayBuilder> EnumArrayBuilder = MakeShareable( new FDetailArrayBuilder( Handle ) );
 				EnumArrayBuilder->OnGenerateArrayElementWidget( FOnGenerateArrayElementWidget::CreateSP( this, &FEnumArrayDetails::OnGenerateArrayRow ) );

				ArrayEnumMap.Add( Property->GetFName(), EnumClass );
				Category.AddCustomBuilder( EnumArrayBuilder );
			}
		}
	}
}

void FEnumArrayDetails::OnGenerateArrayRow( TSharedRef<IPropertyHandle> ElementHandle, int32 Index, IDetailChildrenBuilder& ChildrenBuilder )
{
	TSharedPtr<IPropertyHandle> ArrayHandle = ElementHandle->GetParentHandle();
	FName ArrayName = ArrayHandle->GetProperty()->GetFName();
	UEnum* EnumClass = ArrayEnumMap.FindChecked( ArrayName );

	if( EnumClass->HasMetaData( TEXT("Hidden"), Index ) )
	{
		return;
	}

	FText DisplayText = EnumClass->GetDisplayNameText( Index );
	FText DisplayName = ( !DisplayText.IsEmpty() ) ? DisplayText : FText::FromString( EnumClass->GetEnumName( Index ) );

	ChildrenBuilder.AddChildContent( TEXT("") )
	.NameContent()
	
		SNew( STextBlock )
		.Text( DisplayName )
		.Font( IDetailLayoutBuilder::GetDetailFont() )
	]
	.ValueContent()
	
		ElementHandle->CreatePropertyValueWidget()
	];
}


I bolded the relevant lines, that’s how you inspect metadata to make decisions on it.

Cheers,
-Camille

Hey, thanks.
I actually know how to create customizations, and other things, I just had no idea where to start with custom meta data, added to UPROPERTY(meta=()).

Thanks for the information Camille, that was exactly what I was after :slight_smile: Funnily enough I started looking into customizing the details panel to create the exact same feature (EnumArray) you created.

@iniside

were you able to figure this out?

There’s no predefined list of metadata tags, so you don’t have to do anything special. Just add UPROPERTY(Meta=(MyMetaTag)) and then test for it with


Property->HasMetaData(TEXT("MyMetaTag"))

I’ve used it for all sorts of stuff. It would be nice if it was possible to add meta tags to properties in the blueprint editor though, that would really open up the possibilities.

As @kamrann say you can use the HasMetaData() function to check if this Property has your metadata, also if you simply need to filter meta in a blueprint dropdown menu use this meta:


meta=(FilterMetaTag="YourMetaToFilter")

Hi Camille, Is this still the case? I just ran into this issue when trying to override the customization for DateTime - it’s not using my customization!

edit: Nevermind, worked it out. Do something like this:
PropertyModule.UnregisterCustomPropertyTypeLayout(“DateTime”);
PropertyModule.RegisterCustomPropertyTypeLayout(“DateTime”, FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FDateTimeCustomization::MakeInstance));

And your plugin’s loading phase MUST be “PostEngineInit”

would this same logic apply to a property value within a function parameter being change by uproperty bool?
i have a function that checks what enum the user has choosen.
Based on the enum choice it would vary which parameters are hidden and which ones are visible