void FFoliageTypeObjectCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
	TSharedPtr<IPropertyHandle> FoliageTypeHandle = PropertyHandle->GetChildHandle("FoliageTypeObject");
	
	// Only allow foliage type assets to be created (i.e. don't show all the factories for blueprints)
	TArray<const UClass*> SupportedClasses;
	SupportedClasses.Add(UFoliageType_InstancedStaticMesh::StaticClass());

	HeaderRow
		.NameContent()
		[
			FoliageTypeHandle->CreatePropertyNameWidget()
		]
		.ValueContent()
		.MinDesiredWidth(250.f)
		.MaxDesiredWidth(0.f)
		[
			SNew(SObjectPropertyEntryBox)
			.PropertyHandle(FoliageTypeHandle)
			.ThumbnailPool(CustomizationUtils.GetThumbnailPool())
			.NewAssetFactories(PropertyCustomizationHelpers::GetNewAssetFactoriesForClasses(SupportedClasses))
			.OnShouldFilterAsset(this, &FFoliageTypeObjectCustomization::OnShouldFilterAsset)
		];

		//PropertyCustomizationHelpers::MakeInsertDeleteDuplicateButton <-- not sure how to get that to show up properly, since the struct doesn't know it's an array
}
void FPrimaryAssetIdCustomization::CustomizeHeader(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
	if (!UAssetManager::IsValid())
	{
		HeaderRow
		.NameContent()
		[
			InStructPropertyHandle->CreatePropertyNameWidget()
		]
		.ValueContent()
		.MinDesiredWidth(250.0f)
		.MaxDesiredWidth(0.0f)
		[
			SNew(STextBlock)
			.Text(LOCTEXT("NoAssetManager", "Enable Asset Manager to edit Primary Asset Ids"))
		];

		return;
	}

	StructPropertyHandle = InStructPropertyHandle;

	const FString& TypeFilterString = StructPropertyHandle->GetMetaData("AllowedTypes");
	if( !TypeFilterString.IsEmpty() )
	{
		TArray<FString> CustomTypeFilterNames;
		TypeFilterString.ParseIntoArray(CustomTypeFilterNames, TEXT(","), true);

		for(auto It = CustomTypeFilterNames.CreateConstIterator(); It; ++It)
		{
			const FString& TypeName = *It;

			AllowedTypes.Add(*TypeName);
		}
	}

	FOnShouldFilterAsset AssetFilter = FOnShouldFilterAsset::CreateStatic(&IAssetManagerEditorModule::OnShouldFilterPrimaryAsset, AllowedTypes);

	// Can the field be cleared
	const bool bAllowClear = !(StructPropertyHandle->GetMetaDataProperty()->PropertyFlags & CPF_NoClear);

	HeaderRow
	.NameContent()
	[
		InStructPropertyHandle->CreatePropertyNameWidget()
	]
	.ValueContent()
	.MinDesiredWidth(250.0f)
	.MaxDesiredWidth(0.0f)
	[
		// Add an object entry box.  Even though this isn't an object entry, we will simulate one
		SNew( SObjectPropertyEntryBox )
		.ObjectPath(this, &FPrimaryAssetIdCustomization::OnGetObjectPath)
		.PropertyHandle(InStructPropertyHandle)
		.ThumbnailPool(StructCustomizationUtils.GetThumbnailPool())
		.OnShouldFilterAsset(AssetFilter)
		.OnObjectChanged(this, &FPrimaryAssetIdCustomization::OnSetObject)
		.AllowClear(bAllowClear)
	];
}
void FStringAssetReferenceCustomization::CustomizeHeader( TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils )
{
	StructPropertyHandle = InStructPropertyHandle;

	FString ClassFilterString = StructPropertyHandle->GetMetaData("AllowedClasses");
	if( !ClassFilterString.IsEmpty() )
	{
		TArray<FString> CustomClassFilterNames;
		ClassFilterString.ParseIntoArray(&CustomClassFilterNames, TEXT(","), true);

		for(auto It = CustomClassFilterNames.CreateConstIterator(); It; ++It)
		{
			const FString& ClassName = *It;

			UClass* Class = FindObject<UClass>(ANY_PACKAGE, *ClassName);
			if(!Class)
			{
				Class = LoadObject<UClass>(nullptr, *ClassName);
			}
			if(Class)
			{
				CustomClassFilters.Add(Class);
			}
		}
	}

	bExactClass = StructPropertyHandle->GetBoolMetaData("ExactClass");

	FOnShouldFilterAsset AssetFilter;
	UClass* ClassFilter = UObject::StaticClass();
	if(CustomClassFilters.Num() == 1 && !bExactClass)
	{
		// If we only have one class to filter on, set it as the class type filter rather than use a filter callback
		// We can only do this if we don't need an exact match, as the class filter also allows derived types
		// The class filter is much faster than the callback as we're not performing two different sets of type tests
		// (one against UObject, one against the actual type)
		ClassFilter = CustomClassFilters[0];
	}
	else if(CustomClassFilters.Num() > 0)
	{
		// Only bind the filter if we have classes that need filtering
		AssetFilter.BindSP(this, &FStringAssetReferenceCustomization::OnShouldFilterAsset);
	}

	// Can the field be cleared
	const bool bAllowClear = !(StructPropertyHandle->GetMetaDataProperty()->PropertyFlags & CPF_NoClear);

	HeaderRow
	.NameContent()
	[
		InStructPropertyHandle->CreatePropertyNameWidget()
	]
	.ValueContent()
	.MinDesiredWidth(250.0f)
	.MaxDesiredWidth(0.0f)
	[
		// Add an object entry box.  Even though this isn't an object entry, we will simulate one
		SNew( SObjectPropertyEntryBox )
		.PropertyHandle( InStructPropertyHandle )
		.ThumbnailPool( StructCustomizationUtils.GetThumbnailPool() )
		.AllowedClass( ClassFilter )
		.OnShouldFilterAsset( AssetFilter )
		.AllowClear( bAllowClear )
	];
}