Here is my IPropertyTypeCustomization subclass:
Header:
#pragma once
#include "IPropertyTypeCustomization.h"
#include "Editor/PropertyEditor/Public/PropertyHandle.h"
#include "Editor/PropertyEditor/Public/DetailWidgetRow.h"
#include "BMove/Classes/Common/TaskSceneManager.h"
#include "Runtime/Slate/Public/Widgets/Input/STextComboBox.h"
#include "TaskComponentData.h"
// Delegate for when this property editor is fully initialized
DECLARE_DELEGATE_OneParam(ReadyDelegate, FTaskComponentDataCustomization *);
// Delegate for when a value has been changed
DECLARE_DELEGATE_OneParam(ValueChangedDelegate, FString);
DECLARE_DELEGATE(OnDestructDelegate);
class FTaskComponentDataCustomization : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance();
FTaskComponentDataCustomization::~FTaskComponentDataCustomization();
/** IPropertyTypeCustomization interface */
virtual void CustomizeHeader(TSharedRef<class IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<class IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
void SetTaskData(TArray<FString> AvailableTasks, FString DefaultTask = "");
void SetTaskSelection(FString selection);
ReadyDelegate MyReadyDelegate;
ValueChangedDelegate MyValueChangedDelegate;
OnDestructDelegate MyDestructionDelegate;
uint32 TaskComponentIdentifier;
void SelectionChanged(TSharedPtr < FString > str, ESelectInfo::Type type);
bool bIsReady;
private:
TSharedPtr<IPropertyHandle> UPropertyTaskNameHandle;
TSharedPtr<IPropertyHandle> UPropertyTaskComponentIdentifierHandle;
TSharedPtr<IPropertyHandle> UPropertyTaskComponentNameHandle;
// Class specific members
TSharedPtr<STextComboBox> ComboBoxPtr;
TArray< TSharedPtr<FString>> TaskNames;
/** Visibility delegate for the various methods of calculating magnitude */
EVisibility GetPropertyVisibility(UProperty* InProperty) const;
};
Source:
#include "../BMovePrivatePCH.h"
#include "BMove/Classes/Components/TaskDataCustomization.h"
#include "Runtime/Slate/Public/Widgets/Input/STextComboBox.h"
#include "DetailWidgetRow.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#define LOCTEXT_NAMESPACE "TaskDataCustomization"
FTaskComponentDataCustomization::~FTaskComponentDataCustomization() {
UPropertyTaskNameHandle.Reset();
UPropertyTaskComponentIdentifierHandle.Reset();
ComboBoxPtr.Reset();
TaskNames.Empty();
MyReadyDelegate.Unbind();
MyValueChangedDelegate.Unbind();
if (MyDestructionDelegate.IsBound())
{
MyDestructionDelegate.Execute();
}
}
#pragma region IPropertyTypeCustomization
TSharedRef<IPropertyTypeCustomization> FTaskComponentDataCustomization::MakeInstance()
{
return MakeShareable(new FTaskComponentDataCustomization());
}
void FTaskComponentDataCustomization::CustomizeHeader(TSharedRef<class IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
uint32 NumChildren;
StructPropertyHandle->GetNumChildren(NumChildren);
for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
{
const TSharedRef< IPropertyHandle > ChildHandle = StructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef();
if (ChildHandle->GetProperty()->GetName() == TEXT("Names"))
{
UPropertyTaskNameHandle = ChildHandle;
}
if (ChildHandle->GetProperty()->GetName() == TEXT("AssociatedTaskComponent")) {
UPropertyTaskComponentIdentifierHandle = ChildHandle;
}
if (ChildHandle->GetProperty()->GetName() == TEXT("AssociatedTaskComponentName")) {
UPropertyTaskComponentNameHandle = ChildHandle;
}
}
check(UPropertyTaskComponentIdentifierHandle.IsValid());
check(UPropertyTaskNameHandle.IsValid());
check(UPropertyTaskComponentNameHandle.IsValid());
FName name;
UPropertyTaskComponentNameHandle->GetValue(name);
uint32 testnum = 0;
UPropertyTaskComponentIdentifierHandle->GetValue(testnum);
TaskComponentIdentifier = FCString::Atoi(*name.ToString());
HeaderRow.NameContent()
[
StructPropertyHandle->CreatePropertyNameWidget(LOCTEXT("Task", "Task Names"), LOCTEXT("IDK", "IDK"), false, true, false)
]
.ValueContent()
.MinDesiredWidth(250)
[
SNew(STextComboBox)
.OptionsSource(&TaskNames)
.OnSelectionChanged(this, &FTaskComponentDataCustomization::SelectionChanged)
];
ComboBoxPtr = StaticCastSharedRef<STextComboBox>(HeaderRow.ValueWidget.Widget);
// Register this with TaskComponent
if (TaskSceneManager::GetInstance()->HookTaskPropertyWithComponent(*this)) {
MyReadyDelegate.Execute(this);
}
bIsReady = true;
}
void FTaskComponentDataCustomization::CustomizeChildren(TSharedRef<class IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
}
#pragma endregion
void FTaskComponentDataCustomization::SetTaskData(TArray<FString> AvailableTasks, FString DefaultTask)
{
TaskNames.Empty();
for (FString TaskName : AvailableTasks) {
TaskNames.Add(TSharedPtr<FString>(new FString(*TaskName)));
}
if (ComboBoxPtr.IsValid()) {
ComboBoxPtr->RefreshOptions();
}
if (!DefaultTask.IsEmpty()) {
for (auto &elem : TaskNames) {
if (elem->Equals(DefaultTask)) {
ComboBoxPtr->SetSelectedItem(elem);
break;
}
}
} else {
ComboBoxPtr->SetSelectedItem(TaskNames[0]);
}
}
void FTaskComponentDataCustomization::SetTaskSelection(FString selection) {
ComboBoxPtr->SetSelectedItem(TSharedPtr<FString>(new FString(*selection)));
}
EVisibility FTaskComponentDataCustomization::GetPropertyVisibility(UProperty* InProperty) const {
return EVisibility::Visible;
}
void FTaskComponentDataCustomization::SelectionChanged(TSharedPtr<FString> str, ESelectInfo::Type type) {
if (MyValueChangedDelegate.IsBound() && str.IsValid()) {
MyValueChangedDelegate.Execute(*str);
}
if (ComboBoxPtr.IsValid()) {
ComboBoxPtr->RefreshOptions();
}
}
#undef LOCTEXT_NAMESPACE
I register this customization with the property editor module and set it up correctly…
This Customization subclass is used for this Struct:
USTRUCT()
struct FTaskComponentData
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere)
TArray<FString> Names;
UPROPERTY(EditAnywhere)
uint32 AssociatedTaskComponent;
UPROPERTY(VisibleAnywhere)
FName AssociatedTaskComponentName;
};
Which is used in a component.
UPROPERTY(VisibleAnywhere, Category = "Planning | Task")
FTaskComponentData AvailableTaskNames;
Next, I try to set up some default values…
ComponentIdentifier = UTaskComponent::index;
AvailableTaskNames.AssociatedTaskComponent = ComponentIdentifier;
AvailableTaskNames.AssociatedTaskComponent = ComponentIdentifier;
AvailableTaskNames.AssociatedTaskComponentName = FName(TEXT("test"));
But in my FTaskComponentDataCustomization, when I try to get the value for the handle for the AssociatedTaskComponentName like this:
FName name;
UPropertyTaskComponentNameHandle->GetValue(name);
name is for some reason “3”. It changes values some times and it is very erratic behavior. Some other times it is 2. What could be the cause of this?