Last active
March 6, 2025 22:21
-
-
Save Ryan-DowlingSoka/6ce3f3bf8a38d9c3f5d44a3e9b4feb39 to your computer and use it in GitHub Desktop.
Example customization for handling a GetOptions style picker inside a struct.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "JoePropertyCustomizations.h" | |
#include "DetailWidgetRow.h" | |
#include "IDetailChildrenBuilder.h" | |
#include "PropertyCustomizationHelpers.h" | |
#include "SSearchableComboBox.h" | |
TSharedRef<IPropertyTypeCustomization> FJoeCustomStructPropertyCustomization::MakeInstance() | |
{ | |
return MakeShareable(new FJoeCustomStructPropertyCustomization); | |
} | |
void FJoeCustomStructPropertyCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle, | |
class FDetailWidgetRow& HeaderRow, | |
IPropertyTypeCustomizationUtils& StructCustomizationUtils) | |
{ | |
if (StructPropertyHandle->HasMetaData(FName(TEXT("ShowOnlyInnerProperties")))) | |
{ | |
return; | |
} | |
HeaderRow.NameContent() | |
[ | |
StructPropertyHandle->CreatePropertyNameWidget() | |
] | |
.ValueContent() | |
[ | |
StructPropertyHandle->CreatePropertyValueWidget() | |
]; | |
} | |
void FJoeCustomStructPropertyCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, | |
class IDetailChildrenBuilder& ChildBuilder, | |
IPropertyTypeCustomizationUtils& StructCustomizationUtils) | |
{ | |
//Change this to your struct and your name for the label property. | |
TSharedPtr<IPropertyHandle> LabelHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FJoeCustomStruct, Label)); | |
uint32 NumProperties = 0; | |
StructPropertyHandle->GetNumChildren(NumProperties); | |
for (uint32 i = 0; i < NumProperties; i++) | |
{ | |
auto ChildHandle = StructPropertyHandle->GetChildHandle(i); | |
if (ChildHandle->GetProperty() == LabelHandle->GetProperty()) | |
{ | |
//Special case the label parameter. | |
auto NameWidget = LabelHandle->CreatePropertyNameWidget(); | |
ChildBuilder.AddCustomRow(LabelHandle->GetPropertyDisplayName()) | |
.NameContent() | |
[ | |
LabelHandle->CreatePropertyNameWidget() | |
] | |
.ValueContent() | |
.MinDesiredWidth(125.f) | |
.MaxDesiredWidth(600.f) | |
[ | |
SAssignNew(ComboBox, SSearchableComboBox) | |
.OptionsSource(&Labels) | |
.OnComboBoxOpening(this, &FJoeCustomStructPropertyCustomization::GenerateLabels, StructPropertyHandle) | |
.OnGenerateWidget(this, &FJoeCustomStructPropertyCustomization::MakeItemWidget) | |
.OnSelectionChanged_Lambda([=](const TSharedPtr<FString> NewChoice, ESelectInfo::Type SelectType) | |
{ | |
if(NewChoice).IsValid()) | |
{ | |
const FScopedTransaction Transaction(TEXT("Set Label Value")); | |
LabelHandle->NotifyPreChange(); | |
LabelHandle->SetValue(*NewChoice); | |
LabelHandle->NotifyPostChange(EPropertyChangeType::ValueSet); | |
} | |
}) | |
.Content() | |
[ | |
SNew(STextBlock) | |
.Text_Lambda([=]() | |
{ | |
FName CurrentValue = NAME_None; | |
LabelHandle->GetValue(CurrentValue); | |
FString CurrentValueString = CurrentValue.ToString(); | |
return FText::FromString(CurrentValueString); | |
}) | |
] | |
]; | |
} | |
else | |
{ | |
//Add any other parameters in the struct. | |
ChildBuilder.AddProperty(ChildHandle.ToSharedRef()); | |
} | |
} | |
} | |
void FJoeCustomStructPropertyCustomization::GenerateLabels(TSharedRef<IPropertyHandle> StructPropertyHandle) | |
{ | |
const TSharedPtr<IPropertyHandle> SUDSScriptHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FJoeCustomStruct, SUDSScript)); | |
UObject* SUDSScriptPtr; | |
const FPropertyAccess::Result Result = SUDSScriptHandle->GetValue(SUDSScriptPtr); | |
Labels.Reset(); | |
if(Result == FPropertyAccess::Success) | |
{ | |
if (USUDSScript* SUDSScript = Cast<USUDSScript>(SUDSScriptPtr)) | |
{ | |
//Early out if SUDSScript isn't set. | |
if (!SUDSScript) return; | |
TArray<FName> LabelNames = SUDSScript->GetLabels(); | |
Algo::Transform(LabelNames, Labels,[&](const FName& InLabel) | |
{ | |
return MakeShared<FString>(InLabel.ToString()); | |
}); | |
} | |
} | |
if(ComboBox.IsValid()) | |
ComboBox->RefreshOptions(); | |
} | |
TSharedRef<SWidget> FJoeCustomStructPropertyCustomization::MakeItemWidget( TSharedPtr<FString> StringItem ) | |
{ | |
check( StringItem.IsValid() ); | |
return SNew(STextBlock) | |
.Text(FText::FromString(*(StringItem.Get()))); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "IPropertyTypeCustomization.h" | |
//Copy this into your Editor BeginModule: | |
//FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor"); | |
//PropertyModule.RegisterCustomPropertyTypeLayout(FJoeCustomStruct::StaticStruct()->GetFName(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FJoeCustomStructPropertyCustomization::MakeInstance) ); | |
//Copy this into your module includes into your Module Editor.Build.cs | |
//"ToolWidgets" | |
//This isn't needed, remove it when you remove the USUDSScript and the FJoeCustomStruct | |
#include "JoePropertyCustomizations.generated.h" | |
//Struct should exist in game code. not here. | |
//SUDSScript class is a stub. | |
class SSearchableComboBox; | |
class SWidget; | |
UCLASS(BlueprintType) | |
class USUDSScript : public UClass | |
{ | |
GENERATED_BODY() | |
public: | |
UFUNCTION() | |
TArray<FName> GetLabels() | |
{ | |
return {FName(TEXT("A")), FName(TEXT("B")), FName(TEXT("C"))}; | |
} | |
}; | |
USTRUCT(BlueprintType) | |
struct FJoeCustomStruct | |
{ | |
GENERATED_BODY() | |
UPROPERTY(EditAnywhere, BlueprintReadWrite) | |
TObjectPtr<UObject> SUDSScript = nullptr; //This would be your SUDSScript ref. | |
UPROPERTY(EditAnywhere, BlueprintReadWrite) | |
FName Label = NAME_None; | |
}; | |
class IDetailLayoutBuilder; //Forward Declaration. | |
class FJoeCustomStructPropertyCustomization : public IPropertyTypeCustomization | |
{ | |
public: | |
static TSharedRef<IPropertyTypeCustomization> MakeInstance(); | |
virtual void CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; | |
virtual void CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; | |
void GenerateLabels(TSharedRef<IPropertyHandle> StructPropertyHandle); | |
TSharedRef<SWidget> MakeItemWidget( TSharedPtr<FString> StringItem ); | |
private: | |
TArray<TSharedPtr<FString>> Labels; | |
TSharedPtr<SSearchableComboBox> ComboBox; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment