Download

Creating Filtered Object Pickers

Sep 14, 2021.Knowledge
When declaring an object UProperty, you can provide a class filter to show only classes with a specific base class via the AllowedClasses metadata specifier:

UPROPERTY(EditAnywhere, meta = (AllowedClasses = “MyBaseClass”))
FSoftObjectPath MyObject;
If you’re looking to apply additional filtering to your object picker, you can do this via a property customization. You’ll first need to wrap your property in a UStruct that you can apply the customization to:

USTRUCT(BlueprintType)
struct FMyObjectWrapperStruct
{
GENERATED_USTRUCT_BODY()

public:
UPROPERTY(EditAnywhere)
FSoftObjectPath MyObjectPicker;
};
Then you’ll write a customization to replace the standard picker with your own, filtered version. By default, the detail panel generates an SPropertyEditorAsset for objects. Your customization will do the same via the SObjectPropertyEntryBox widget, allowing you more control over the settings of the generated widget:

class FMyWrapperStructCustomization
: public IPropertyTypeCustomization
{
public:

/**
 * Creates an instance of this class.
 *
 * @return The new instance.
 */
static TSharedRef<IPropertyTypeCustomization> MakeInstance()
{
	return MakeShareable(new FMyWrapperStructCustomization());
}

public:

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

protected:
bool OnShouldFilterAsset(const struct FAssetData& InAssetData);

};

We’ll also need to register this customization with the property editor. This is often done in StartupModule for whichever module the struct is defined in:

static FName PropertyEditor(“PropertyEditor”);
static FName WrapperClass(“MyObjectWrapperStruct”);
FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked(PropertyEditor);
PropertyModule.RegisterCustomPropertyTypeLayout(WrapperClass, FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMyWrapperStructCustomization::MakeInstance));
Now, we can write our customization class. We’ll use CustomizeHeader to create our own picker widget, and hook up a filter delegate so that we have control over which entries appear:

void FMyWrapperStructCustomization::CustomizeChildren(TSharedRef StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
// Nothing to do here, we don’t want any child properties to appear (we’ll display the picker directly on the header row instead)
}

void FMyWrapperStructCustomization::CustomizeHeader(TSharedRef StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{

TSharedPtr<IPropertyHandle> MyProperty = StructPropertyHandle->GetChildHandle(TEXT("MyObjectPicker"));

HeaderRow
.NameContent()
[
	StructPropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
	SNew(SObjectPropertyEntryBox)
	.PropertyHandle(MyProperty)
	.OnShouldFilterAsset(FOnShouldFilterAsset::CreateRaw(this, &FMyWrapperStructCustomization::OnShouldFilterAsset)) // Custom filter delegate for additional filtering
];

}

bool FMyWrapperStructCustomization::OnShouldFilterAsset(const struct FAssetData& InAssetData)
{
// Inspect the asset data to determine if the row should be filtered (true) or displayed (false)
return false;
}
Now that you have your own picker, you can explore the other parameters exposed on SObjectPropertyEntryBox to further customize the display and filtering of your picker.