Custom blueprint details workflow for custom data type

I’m working on a board game that has different types of cards. One of my card type i have a long list of Enum flags that you can add that will flag the card with abilities for the game logic to process. One of the flags is to give the player a specific card with the specified name.

What I want to be able to do is while creating the card blueprints is if you select the flag in the details panel to have the specific card flag the string property (which each card will have regardless of the flag) will become either visible or editable, otherwise it isn’t. I know i could just have the string editable all the time since it wont be used if the flag isn’t set but I would like a cleaner approach and only have the property visible / editable under certain conditions.

How do I accomplish this?

Changing editability is pretty easy: Override CanEditChange. Assuming that your enum flags is used as such, a bitmask of flags:


bool UMyGameCard::CanEditChange( const UProperty* InProperty ) const 
{
	bool bIsEditable = Super::CanEditChange( InProperty );
	if( bIsEditable && InProperty != NULL )
	{
		const FName PropertyName = InProperty->GetFName();
		
		// Randomizing variant without randomizing gender is okay, but
		// randomizing gender without randomizing variant makes no sense
		if( PropertyName == GET_MEMBER_NAME_CHECKED( UMyGameCard, CardFlags ) )
		{
			bIsEditable = ( CardFlags & ECardFlags::GivesCard ) != 0;
		}
	}

	return bIsEditable;
}

If it’s just a bool flag, simply replace the bitmask check with a bool check.

Changing visibility is a bit trickier but still very straightforward once you get the hang of editor details customizations. Details customization, as the name says, lets you control how your properties are being displayed. It’s an incredibly powerful feature and there thankfully is plenty of examples in the form of the editor’s own source code. But basically, you need to create a class customization for your card class, then bind a delegate to your card name’s visibility.

First, you need to register the class customization:


void FMyGameEditorModule::StartupModule()
{
	FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>( "PropertyEditor" );
	PropertyModule.RegisterCustomClassLayout( "MyGameCard", FOnGetDetailCustomizationInstance::CreateStatic( &FMyGameCardDetails::MakeInstance ) );
	PropertyModule.NotifyCustomizationModuleChanged();
}

If you don’t already have an editor module, you’ll need to create one as the property editor module pulls editor dependencies you cannot have in a game module.

Then, the actual implementation:


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

void FMyGameCardDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{
	// This needs to be whatever category your card flag is using
	IDetailCategoryBuilder& CardFlagsCategory = DetailBuilder.EditCategory( "CardFlags" );  
	
	// Store this property in a TSharedPtr<IPropertyHandle> member variable for access within the delegate
	CardFlagsProperty = DetailBuilder.GetProperty( GET_MEMBER_NAME_CHECKED( UMyGameCard, CardFlags ) ); 
	TSharedPtr<IPropertyHandle> GivenCardNameProperty = DetailBuilder.GetProperty( GET_MEMBER_NAME_CHECKED( UMyGameCard, GivenCardName ) );
	
	CardFlagsCategory.AddProperty( GivenCardNameProperty )
	.Visibility( TAttribute<EVisibility>::Create( TAttribute<EVisibility>::FGetter::CreateSP( this, &FMyGameCardDetails::GetGivenCardNameVisibility ) );
}

EVisibility FMyGameCardDetails::GetGivenCardNameVisibility() const
{
	ECardFlags::Type CardFlagsValue;
	if( CardFlagsProperty->GetValue( CardFlagsValue ) == FPropertyAccess::Success )
	{
		if( ( CardFlagsValue & ECardFlags::GivesCard ) != 0 )
		{
			return EVisibility::Visible;
		}
	}
	
	return EVisibility::Collapsed;
}

An important thing to note when dealing with details customization: You want to manipulate your properties through the provided IPropertyHandles. The reason is that they allow you to handle multiple selection trivially. If you have multiple customized objects selected, IPropertyHandle::GetValue will return FPropertyAccess::Success if the value is the same and therefore can be displayed, or FPropertyAccess::MultipleValues if the values differ. In the above example, if you were to somehow select multiple cards and they did not all have the same flags, then the FPropertyAccess::Success conditional would fail and the card name property would thus disappear.

This should get you started if you want to go down the route of editor customization.