Files
UnrealEngine/Engine/Plugins/Developer/AnimationSharing/Source/AnimationSharingEd/Private/SetupDetailsViewCustomizations.cpp
2025-05-18 13:04:45 +08:00

415 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SetupDetailsViewCustomizations.h"
#include "DetailWidgetRow.h"
#include "IDetailChildrenBuilder.h"
#include "Widgets/Input/STextComboBox.h"
#include "AnimationSharingTypes.h"
#define LOCTEXT_NAMESPACE "AnimationSharingSetupCustomization"
TSharedRef<IPropertyTypeCustomization> FPerSkeletonAnimationSharingSetupCustomization::MakeInstance()
{
return MakeShareable(new FPerSkeletonAnimationSharingSetupCustomization());
}
void FPerSkeletonAnimationSharingSetupCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
SkeletonPropertyHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPerSkeletonAnimationSharingSetup, Skeleton));
if (SkeletonPropertyHandle.IsValid())
{
HeaderRow
.NameContent()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(1)
.VAlign(VAlign_Center)
[
// Show the name of the asset or actor
SNew(STextBlock)
.Font(FAppStyle::GetFontStyle("PropertyWindow.NormalFont"))
.Text(this, &FPerSkeletonAnimationSharingSetupCustomization::GetSkeletonName)
]
];
}
}
void FPerSkeletonAnimationSharingSetupCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
uint32 NumChildren;
StructPropertyHandle->GetNumChildren(NumChildren);
const TArray<FName> SkeletonDisabledProperties =
{
GET_MEMBER_NAME_CHECKED(FPerSkeletonAnimationSharingSetup, SkeletalMesh),
GET_MEMBER_NAME_CHECKED(FPerSkeletonAnimationSharingSetup, StateProcessorClass),
GET_MEMBER_NAME_CHECKED(FPerSkeletonAnimationSharingSetup, BlendAnimBlueprint),
GET_MEMBER_NAME_CHECKED(FPerSkeletonAnimationSharingSetup, AdditiveAnimBlueprint)
};
const TArray<FName> EnumDisabledProperties =
{
GET_MEMBER_NAME_CHECKED(FPerSkeletonAnimationSharingSetup, AnimationStates)
};
TSharedPtr<IPropertyHandle> ProcessorProperty = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPerSkeletonAnimationSharingSetup,
StateProcessorClass));
void* StructPtr = nullptr;
StructPropertyHandle->GetValueData(StructPtr);
for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
{
TSharedRef<IPropertyHandle> ChildHandle = StructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef();
IDetailPropertyRow& Property = StructBuilder.AddProperty(ChildHandle);
const FName& PropertyName = ChildHandle->GetProperty()->GetFName();
/** Properties disabled by an invalid USkeleton */
if (SkeletonDisabledProperties.Contains(PropertyName))
{
Property.IsEnabled(TAttribute<bool>::Create([this]() -> bool
{
UObject* Object = nullptr;
return (SkeletonPropertyHandle->GetValue(Object) == FPropertyAccess::Success) && (Object != nullptr);
}));
}
/** Properties disabled by invalid UEnum class */
if (EnumDisabledProperties.Contains(PropertyName))
{
Property.IsEnabled(TAttribute<bool>::Create([ProcessorProperty]() -> bool
{
const UEnum* EnumClass = GetStateEnumClass(ProcessorProperty);
return (EnumClass != nullptr);
}));
}
/** Disable additive Anim BP property if there aren't any additive states in the setup */
if (StructPtr)
{
if (PropertyName == GET_MEMBER_NAME_CHECKED(FPerSkeletonAnimationSharingSetup, AdditiveAnimBlueprint))
{
Property.IsEnabled(TAttribute<bool>::Create([StructPtr]() -> bool
{
FPerSkeletonAnimationSharingSetup* SetupStruct = (FPerSkeletonAnimationSharingSetup*)StructPtr;
if (SetupStruct)
{
const bool bContainsAdditive = SetupStruct->AnimationStates.ContainsByPredicate([](FAnimationStateEntry& Entry) -> bool
{
return Entry.bAdditive;
});
return bContainsAdditive;
}
return false;
}));
}
}
}
}
FText FPerSkeletonAnimationSharingSetupCustomization::GetSkeletonName() const
{
UObject* SkeletonObject;
FPropertyAccess::Result Result = SkeletonPropertyHandle->GetValue(SkeletonObject);
FText Name = LOCTEXT("None", "None");
if (Result == FPropertyAccess::Success)
{
if (SkeletonObject)
{
Name = FText::AsCultureInvariant(SkeletonObject->GetName());
}
}
return Name;
}
TSharedRef<IPropertyTypeCustomization> FAnimationStateEntryCustomization::MakeInstance()
{
return MakeShareable(new FAnimationStateEntryCustomization());
}
void FAnimationStateEntryCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
TSharedPtr<IPropertyHandle> ParentHandle = PropertyHandle->GetParentHandle()->GetParentHandle();
// We make the assumption here that the parent handle is the array part of the FPerSkeletonAnimationSharingSetup
ProcessorPropertyHandle = ParentHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FPerSkeletonAnimationSharingSetup, StateProcessorClass));
StatePropertyHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FAnimationStateEntry, State));
if (StatePropertyHandle.IsValid())
{
HeaderRow
.NameContent()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(1)
.VAlign(VAlign_Center)
[
// Show the name of the asset or actor
SNew(STextBlock)
.Font(FAppStyle::GetFontStyle("PropertyWindow.NormalFont"))
.Text(this, &FAnimationStateEntryCustomization::GetStateName, StatePropertyHandle)
]
];
}
}
void FAnimationStateEntryCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
uint32 NumChildren;
StructPropertyHandle->GetNumChildren(NumChildren);
const TArray<FName> OnDemandProperties = { GET_MEMBER_NAME_CHECKED(FAnimationStateEntry, bReturnToPreviousState), GET_MEMBER_NAME_CHECKED(FAnimationStateEntry, bSetNextState), GET_MEMBER_NAME_CHECKED(FAnimationStateEntry, NextState), GET_MEMBER_NAME_CHECKED(FAnimationStateEntry, WiggleTimePercentage) };
const TArray<FName> EnumProperties = { GET_MEMBER_NAME_CHECKED(FAnimationStateEntry, State), GET_MEMBER_NAME_CHECKED(FAnimationStateEntry, NextState) };
TSharedPtr<IPropertyHandle> OnDemandHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FAnimationStateEntry, bOnDemand));
TSharedPtr<IPropertyHandle> AdditiveHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FAnimationStateEntry, bAdditive));
for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
{
TSharedRef<IPropertyHandle> ChildHandle = StructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef();
// Hide any on-demand settings when either the state is not an on-demand or it is but an additive as well
TAttribute<EVisibility> VisibilityAttribute = TAttribute<EVisibility>::Create([OnDemandHandle, AdditiveHandle]() -> EVisibility
{
if (OnDemandHandle.IsValid() && AdditiveHandle.IsValid())
{
bool bOnDemandValue = false;
OnDemandHandle->GetValue(bOnDemandValue);
bool bAdditiveValue = false;
AdditiveHandle->GetValue(bAdditiveValue);
return bOnDemandValue && !bAdditiveValue ? EVisibility::Visible : EVisibility::Collapsed;
}
return EVisibility::Visible;
});
if (EnumProperties.Contains(ChildHandle->GetProperty()->GetFName()))
{
FDetailWidgetRow& WidgetRow = CreateEnumSelectionWidget(ChildHandle, StructBuilder);
if (OnDemandProperties.Contains(ChildHandle->GetProperty()->GetFName()))
{
WidgetRow.Visibility(VisibilityAttribute);
}
}
else
{
IDetailPropertyRow& PropertyRow = StructBuilder.AddProperty(ChildHandle);
if (OnDemandProperties.Contains(ChildHandle->GetProperty()->GetFName()))
{
PropertyRow.Visibility(VisibilityAttribute);
}
}
}
}
FText FAnimationStateEntryCustomization::GetStateName(TSharedPtr<IPropertyHandle> PropertyHandle) const
{
uint8 EnumValue;
FPropertyAccess::Result Result = PropertyHandle->GetValue(EnumValue);
FText Name = LOCTEXT("None", "None");
if (Result == FPropertyAccess::Success)
{
if (UEnum* EnumClass = GetStateEnumClass(ProcessorPropertyHandle))
{
Name = EnumClass->GetDisplayNameTextByIndex(EnumValue);
}
else
{
FFormatNamedArguments Args;
Args.Add(TEXT("EnumIndex"), EnumValue);
Name = FText::Format(LOCTEXT("EnumIndexValue", "Enum Index {EnumIndex}"), Args);
}
}
return Name;
}
FDetailWidgetRow& FAnimationStateEntryCustomization::CreateEnumSelectionWidget(TSharedRef<IPropertyHandle> ChildHandle, IDetailChildrenBuilder& StructBuilder)
{
GenerateEnumComboBoxItems();
TSharedPtr<FString> CurrentlySelected = GetSelectedEnum(ChildHandle);
FDetailWidgetRow& DetailRow = StructBuilder.AddCustomRow(LOCTEXT("EnumStateSearchLabel", "State"))
.NameContent()
[
ChildHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
[
SNew(STextComboBox)
.OptionsSource(&ComboBoxItems)
.InitiallySelectedItem(CurrentlySelected)
.OnSelectionChanged(this, &FAnimationStateEntryCustomization::SelectedEnumChanged, ChildHandle)
.Font(FAppStyle::GetFontStyle("PropertyWindow.NormalFont"))
]
];
return DetailRow;
}
const TArray<TSharedPtr<FString>> FAnimationStateEntryCustomization::GetComboBoxSourceItems() const
{
TArray<TSharedPtr<FString>> Items;
if (UEnum* EnumClass = GetStateEnumClass(ProcessorPropertyHandle))
{
const int32 NumEnums = EnumClass->NumEnums();
for (int32 Index = 0; Index < NumEnums; ++Index)
{
Items.Add(MakeShareable(new FString(EnumClass->GetDisplayNameTextByIndex(Index).ToString())));
}
}
return Items;
}
const TSharedPtr<FString> FAnimationStateEntryCustomization::GetSelectedEnum(TSharedPtr<IPropertyHandle> PropertyHandle) const
{
const TSharedPtr<FString>* StringPtr = ComboBoxItems.FindByPredicate([this, PropertyHandle](TSharedPtr<FString> SharedString) {
return *SharedString == GetStateName(PropertyHandle).ToString();
});
return StringPtr != nullptr ? *StringPtr : MakeShareable(new FString(GetStateName(PropertyHandle).ToString()));
}
void FAnimationStateEntryCustomization::SelectedEnumChanged(TSharedPtr<FString> Selection, ESelectInfo::Type SelectInfo, TSharedRef<IPropertyHandle> PropertyHandle)
{
if (UEnum* EnumClass = GetStateEnumClass(ProcessorPropertyHandle))
{
if (Selection && SelectInfo != ESelectInfo::Type::Direct)
{
const uint8 NewEnumValue = IntCastChecked<uint8>(EnumClass->GetValueByIndex(ComboBoxItems.IndexOfByKey(Selection)));
PropertyHandle->SetValue(NewEnumValue);
}
}
}
void FAnimationStateEntryCustomization::GenerateEnumComboBoxItems()
{
if (UEnum* EnumClass = GetStateEnumClass(ProcessorPropertyHandle))
{
if (EnumClass != CachedComboBoxEnumClass)
{
ComboBoxItems.Empty();
const int32 NumEnums = EnumClass->NumEnums();
for (int32 Index = 0; Index < NumEnums; ++Index)
{
ComboBoxItems.Add(MakeShareable(new FString(EnumClass->GetDisplayNameTextByIndex(Index).ToString())));
}
CachedComboBoxEnumClass = EnumClass;
}
}
}
UEnum* GetStateEnumClass(const TSharedPtr<IPropertyHandle>& InProperty)
{
UObject* EnumObject = nullptr;
if (InProperty.IsValid())
{
UObject* ProcessorObject = nullptr;
InProperty->GetValue(ProcessorObject);
UClass* ProcessorClass = Cast<UClass>(ProcessorObject);
if (ProcessorClass)
{
UAnimationSharingStateProcessor* Processor = ProcessorClass->GetDefaultObject<UAnimationSharingStateProcessor>();
return Processor->GetAnimationStateEnum();
}
}
return nullptr;
}
TSharedRef<IPropertyTypeCustomization> FAnimationSetupCustomization::MakeInstance()
{
return MakeShareable(new FAnimationSetupCustomization());
}
void FAnimationSetupCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
const FName AnimSequencePropertyName = GET_MEMBER_NAME_CHECKED(FAnimationSetup, AnimSequence);
AnimSequencePropertyHandle = PropertyHandle->GetChildHandle(AnimSequencePropertyName);
HeaderRow
.NameContent()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(1)
.VAlign(VAlign_Center)
[
// Show the name of the asset or actor
SNew(STextBlock)
.Font(FAppStyle::GetFontStyle("PropertyWindow.NormalFont"))
.Text_Lambda([this]() -> FText
{
if (AnimSequencePropertyHandle.IsValid())
{
FText PropertyValueAsName;
if (AnimSequencePropertyHandle->GetValueAsFormattedText(PropertyValueAsName) == FPropertyAccess::Success)
{
return PropertyValueAsName;
}
}
return LOCTEXT("None", "None");
})
]
];
/*.ValueContent()
[
PropertyHandle->CreatePropertyValueWidget()
];*/
}
void FAnimationSetupCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
uint32 NumChildren;
StructPropertyHandle->GetNumChildren(NumChildren);
const FName AnimSequencePropertyName = GET_MEMBER_NAME_CHECKED(FAnimationSetup, AnimSequence);
AnimSequencePropertyHandle = StructPropertyHandle->GetChildHandle(AnimSequencePropertyName);
for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
{
TSharedRef<IPropertyHandle> ChildHandle = StructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef();
IDetailPropertyRow& Property = StructBuilder.AddProperty(ChildHandle);
/** Disable all properties if there is not a valid Animation Sequence provided */
if (AnimSequencePropertyHandle.IsValid() && (ChildHandle->GetProperty()->GetFName() != AnimSequencePropertyName))
{
Property.IsEnabled(TAttribute<bool>::Create([this]() -> bool
{
if ( AnimSequencePropertyHandle.Get())
{
UObject* ObjectPtr = nullptr;
if (AnimSequencePropertyHandle->GetValue(ObjectPtr) == FPropertyAccess::Success)
{
return ObjectPtr != nullptr;
}
}
return false;
}));
}
}
}
#undef LOCTEXT_NAMESPACE // "AnimationSharingSetupCustomization"