Where is ILayoutDetails?

I already made a thread that’s more specific here regarding what I want to do with this all.

However, I’ve continued to try and work on this, and one of the most glaring issues with the documentation found here is that nothing seems to use ILayoutDetails. In fact, overall the documentation given in that link seems sorely lacking - it mentions nothing of the many dependencies just getting the first two lines to compile at all.

Anyway, I’ve been scanning over DetailCustomization.cpp, and I can’t seem to find a class that uses ILayoutDetails. I.e Actor uses IDetailCustomization instead…

This is all a bit confusing when trying to learn the basics of messing with the details panel.

1 Like

Coming back to answer this myself after a lot of headbashing…

We’re going to cover a few things here:

  • Opening note
  • ILayoutDetails
  • Creating your (property type) customization
  • Your IPropertyTypeCustomization class
  • Customizing your property type
  • Notes

Opening note

I was creating a customization for FName. For this reason, most of this is going to concern itself with IPropertyTypeCustomization.

If you need to see the difference between these, I suggest scanning over the DetailCustomizations.cpp. You’ll find that here:

C:\Program Files\Epic Games\UE_4.15\Engine\Source\Editor\DetailCustomizations\Private\DetailCustomizations.cpp
  • IPropertyType examples - check out FDetailCustomizationsModule::RegisterPropertyTypeCustomizations.
  • IDetailCustomization examples - check out FDetailCustomizationsModule::RegisterObjectCustomizations.

Make sure to study any class you think is relevant. It’s quite a headache to figure this out on your own.

ILayoutDetails

The class that the documentation asks you to use does not exist, and from what I can tell it hasn’t existed for a very long time. In its place, however, we have two new types: IPropertyTypeCustomization and IDetailCustomization.

IPropertyTypeCustomization allows you to define how default values are made for single variable types. For example, my FName customization allowed me to make a small change to FName:

129877-fnamemoduleshowcasenomod.png

vs

129878-fnamemoduleshowcasemod.png

IDetailCustomization is for when you have more complex things to put together, and need a simpler interface for the entire ordeal. For this, let’s look at the actor picker.

129879-actorcustomization.png

Creating your (property type) customization

Firstly, you should have a Module - in my case, all it does is my binding. I suggest just creating a Blank module from Edit → Plugins → New Plugin if you do not already have one.

Within your module’s .uplugin, you’ll need to make the following change so that your module is loaded in the editor.

"Modules": [
	{
		"Name": "YourModuleName",
		"Type": "Editor",
		"LoadingPhase": "Default"
	}
]

Within your module’s .h file, you’ll need the following:

#include "CoreMinimal.h"
#include "Modules/ModuleInterface.h"
#include "Modules/ModuleManager.h"
#include "PropertyEditorModule.h"
#include "PropertyEditorDelegates.h"
#include "DetailCustomizations.h"

//Your IPropertyTypeCustomization or IDetailCustomization
#include "YourDetailCustomization.h"

– You may not need all of them. I went a bit ham. If any of them are redundant, do tell!

In your .cpp file you should have a function called StartupModule, and ShutdownModule. Within both you’ll need to declare a PropertyModule - this is needed to register your customizations!

In my case, I’ll be describing IPropertyTypeCustomization’s process.

FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");

And the StartupModule and ShutdownModule need one more line, respectively.

In StartupModule()…

PropertyModule.RegisterCustomPropertyTypeLayout(
      PropertyToCustomize,
      FOnGetPropertyTypeCustomizationInstance::CreateStatic(
            &YourCustomizationClass::MakeInstance)
      );

And our ShutdownModule() needs its counterpart:

PropertyModule.UnregisterCustomPropertyTypeLayout(
      PropertyToCustomize
      );

It’ll be up to you to find the name of the property you want to change. In my case, I wanted to change FName, which required me to use NAME_NameProperty.

Your IPropertyTypeCustomization class

Firstly, we need a function that matches what we put into our above binding - MakeInstance. All it does is return an instance on request.

Additionally, we’ll need two functions to receive data from Unreal Engine’s back-end:

  • CustomizeHeader
  • CustomizeChildren

There is no real reason that I know as to why you shouldn’t just copy-paste the following and modify it to your liking.

class YourCustomization: public IPropertyTypeCustomization
{
public:
	/**
	* Creates a new instance.
	*
	* @return A new property type customization.
	*/
	static TSharedRef<IPropertyTypeCustomization> MakeInstance()
	{
		return MakeShareable(new YourCustomization());
	}

public:
	// IPropertyTypeCustomization interface
	virtual void CustomizeHeader(
		TSharedRef<IPropertyHandle> StructPropertyHandle, 
		class FDetailWidgetRow& HeaderRow,
		IPropertyTypeCustomizationUtils& StructCustomizationUtils
	) override;

	virtual void CustomizeChildren(
		TSharedRef<class IPropertyHandle> StructPropertyHandle, 
		class IDetailChildrenBuilder& StructBuilder, 
		IPropertyTypeCustomizationUtils& StructCustomizationUtils
	) override;

private:
    TSharedPtr<IPropertyHandle> PropertyHandle;
}

Customizing your property type

This is when we’ll finally go into your class’ .cpp file.

Firstly, we’ll need to include Slate. I just copy-pasted from another class, so mine looks like this:

//Copypaste from RangeStructCustomization
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Engine/GameViewportClient.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Layout/SSpacer.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Input/SComboBox.h"
#include "DetailWidgetRow.h"
#include "Editor.h"
#include "PropertyHandle.h"
#include "DetailLayoutBuilder.h"
#include "Widgets/Input/SNumericEntryBox.h"
#include "SlateBasics.h"

Again, I’m unlikely to need all of those. I just haven’t gone through and found which ones are redundant… :slight_smile:

Remember the two functions, CustomizeHeader and CustomizeChildren? They’ll come in handy now:

void FNameDetailCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle, FDetailWidgetRow & HeaderRow, IPropertyTypeCustomizationUtils & StructCustomizationUtils)
{
	PropertyHandle = StructPropertyHandle;
	HeaderRow.NameContent()
	[
		StructPropertyHandle->CreatePropertyNameWidget()
	]
	[
		SNew(STextblock)
		.Text(FText::FromString(TEXT("Your property has been replaced by plaintext!"))
	]
}

void FNameDetailCustomization::CustomizeChildren(
	TSharedRef<class IPropertyHandle> StructPropertyHandle, 
	IDetailChildrenBuilder & StructBuilder, 
	IPropertyTypeCustomizationUtils & StructCustomizationUtils
)
{
	//do nothing
}

If you’ve made it this far, your challenge is now Slate, and what you can bind to those elements. For example, I have an SEditableTextBox…

SNew(SEditableTextBox)
.Text(this, &FNameDetailCustomization::GetNameValue)
.OnTextChanged(this,&FNameDetailCustomization::UpdateValue)
.OnTextCommitted(this,&FNameDetailCustomization::SetNameValue)

Notes

  • You can find the code I wrote when learning how to do this [here][4].
  • A PropertyType customization should be seen on your variable, and if it’s part of an array.
  • However, a TMap will override your customization it seems.
3 Likes

(+ 1) to question and answer for great sleuthing.
Thanks so much, cant express how frustrating the docs can be sometimes.

1 Like

Thanks! The documentation still says to inherit from ILayoutDetails but all I could see was IDetailCustomization.

I very much appreciate the effort you went to to figure this out - and then the further effort to post it here for us to learn from.