IDetailCustomization - any tips for using it?

Hey! Actually, I was able to get things going using the wiki: A new, community-hosted Unreal Engine Wiki - Announcements and Releases - Unreal Engine Forums
Also, the docs (https://docs.unrealengine.com/latest…Customization/) while outdated (the code won’t work I think), it requires minor modifications to work.

Here is some of my code which will help you find what exactly is outdated in the docs (and with what you should replace it!)



/*  .h */

#include "CoreMinimal.h"
#include "IDetailCustomization.h"
class IPropertyHandle;
class IDetailChildrenBuilder;

class FResourceGeneratorDetails : public IDetailCustomization
{
public:

    static TSharedRef<IDetailCustomization> MakeInstance();

    virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
protected:

    void GenerateRecipeArrayElementWidget(TSharedRef<IPropertyHandle> ChildHandle, int32 ArrayIndex, IDetailChildrenBuilder& ChildrenBuilder, IDetailLayoutBuilder* DetailLayout);

private:

    void ShareRecipeKeys();

    TArray<TSharedPtr<FName>> RecipeKeys;

    //TSharedPtr<IPropertyHandle> CurrentHandle;
};


and the cpp:



...
#include "DetailCategoryBuilder.h"
#include "DetailLayoutBuilder.h"
#include "IDetailChildrenBuilder.h"
#include "IDetailPropertyRow.h"

#include "PropertyHandle.h"

#include "DetailWidgetRow.h"
#include "STextBlock.h"
#include "SComboBox.h"
#include "SWidget.h"
#include "PropertyCustomizationHelpers.h"


#define LOCTEXT_NAMESPACE "ResourceGeneratorDetails"


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

void FResourceGeneratorDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
    ShareRecipeKeys();
    // Create a category so this is displayed early in the properties
    IDetailCategoryBuilder& CraftingCategory = DetailBuilder.EditCategory("Crafting", FText::FromString(TEXT("Crafting")), ECategoryPriority::Important);

    //You can get properties using the detailbuilder
    //MyProperty= DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(MyClass, MyClassPropertyName));
    TSharedPtr<IPropertyHandle> RecipesProperty = DetailBuilder.GetProperty("Recipes");
    TSharedRef<FDetailArrayBuilder> RecipesArrayPropertyBuilder = MakeShareable(new FDetailArrayBuilder(RecipesProperty.ToSharedRef()));

    RecipesArrayPropertyBuilder->OnGenerateArrayElementWidget(FOnGenerateArrayElementWidget::CreateSP(this, &FResourceGeneratorDetails::GenerateRecipeArrayElementWidget, &DetailBuilder));
    CraftingCategory.AddCustomBuilder(RecipesArrayPropertyBuilder, false);
}


void FResourceGeneratorDetails::GenerateRecipeArrayElementWidget(TSharedRef<IPropertyHandle> ChildHandle, int32 ArrayIndex, IDetailChildrenBuilder& ChildrenBuilder, IDetailLayoutBuilder* DetailLayout)
{

    IDetailPropertyRow& PropertyArrayRow = ChildrenBuilder.AddProperty(ChildHandle);

    PropertyArrayRow.CustomWidget()
    .NameContent()
    
        ChildHandle->CreatePropertyNameWidget()
    ]
    .ValueContent()
    
        SNew(SHorizontalBox)
        + SHorizontalBox::Slot()
        
            SNew(SComboBox<TSharedPtr<FName>>)
            .OptionsSource(&RecipeKeys)
            .OnGenerateWidget_Lambda ( ](TSharedPtr<FName> InItem) {
                return SNew(STextBlock)
                .Text(FText::FromName(*InItem));
            })
            .OnSelectionChanged_Lambda(=](TSharedPtr<FName> Selection, ESelectInfo::Type){
                if (ChildHandle->IsValidHandle()) {
                    ChildHandle->SetValue(*Selection);
                }
            })
            .ContentPadding(FMargin(2, 0))
            
                SNew(STextBlock)
                .Font(IDetailLayoutBuilder::GetDetailFont())
                .Text_Lambda( =]() -> FText
                {
                    if (ChildHandle->IsValidHandle())
                    {
                        FName val;
                        ChildHandle->GetValue(val);
                        return FText::FromName(val);
                    }

                    return FText::GetEmpty();
                } )
            ]
        ]
    ];

}

void FResourceGeneratorDetails::ShareRecipeKeys()
{
    TArray<FName> AllKeys = UETPStatics::GetCraftingRecipeKeys();
    RecipeKeys.Empty(AllKeys.Num());

    for (FName key : AllKeys) {
        RecipeKeys.Add(MakeShareable(new FName(key)));
    }
}


#undef LOCTEXT_NAMESPACE


Excuse me for not cleaning up the code; nevertheless, I think you will find this (more complicated) example more useful since it is a collection of various tutorials/digging through engine code.
What it actually does is create a drop-down menu for values in an ARRAY. The values that are shown are found in GetCraftingRecipeKeys() so ignore (or replace) it.

For something simple you should just keep the CustomizeDetails()

Don’t forget to register your customization through StartupModule() using:



    PropertyModule.RegisterCustomClassLayout("ResourceGenerator", FOnGetDetailCustomizationInstance::CreateStatic(&FResourceGeneratorDetails::MakeInstance));


If you don’t have a module yet you should follow this:

Hope I helped a bit, good luck :slight_smile: