Files
UnrealEngineUWP/Engine/Source/Editor/AnimGraph/Private/AnimGraphNode_CustomProperty.cpp
Marc Audy 6fb39af50b Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 6662737
#rb
#rnx

[CL 6664141 by Marc Audy in Dev-Framework branch]
2019-05-29 11:33:11 -04:00

420 lines
12 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "AnimGraphNode_CustomProperty.h"
#include "EdGraphSchema_K2.h"
#include "Kismet2/CompilerResultsLog.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "DetailLayoutBuilder.h"
#include "AnimationGraphSchema.h"
#include "Animation/AnimInstance.h"
#include "Animation/AnimLayerInterface.h"
#include "DetailCategoryBuilder.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/SBoxPanel.h"
#include "DetailWidgetRow.h"
#include "PropertyCustomizationHelpers.h"
#define LOCTEXT_NAMESPACE "CustomPropNode"
void UAnimGraphNode_CustomProperty::ValidateAnimNodeDuringCompilation(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog)
{
Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog);
UAnimBlueprint* AnimBP = CastChecked<UAnimBlueprint>(GetBlueprint());
UObject* OriginalNode = MessageLog.FindSourceObject(this);
if(NeedsToSpecifyValidTargetClass())
{
// Check we have a class set
UClass* TargetClass = GetTargetClass();
if(!TargetClass)
{
MessageLog.Error(TEXT("Sub instance node @@ has no valid instance class to spawn."), this);
}
}
}
void UAnimGraphNode_CustomProperty::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{
Super::ReallocatePinsDuringReconstruction(OldPins);
// Grab the SKELETON class here as when we are reconstructed during during BP compilation
// the full generated class is not yet present built.
UClass* TargetClass = GetTargetSkeletonClass();
if(!TargetClass)
{
// Nothing to search for properties
return;
}
// Need the schema to extract pin types
const UEdGraphSchema_K2* Schema = CastChecked<UEdGraphSchema_K2>(GetSchema());
// Default anim schema for util funcions
const UAnimationGraphSchema* AnimGraphDefaultSchema = GetDefault<UAnimationGraphSchema>();
// Grab the list of properties we can expose
TArray<UProperty*> ExposablePropeties;
GetExposableProperties(ExposablePropeties);
// We'll track the names we encounter by removing from this list, if anything remains the properties
// have been removed from the target class and we should remove them too
TArray<FName> BeginExposableNames = KnownExposableProperties;
for(UProperty* Property : ExposablePropeties)
{
FName PropertyName = Property->GetFName();
BeginExposableNames.Remove(PropertyName);
if(!KnownExposableProperties.Contains(PropertyName))
{
// New property added to the target class
KnownExposableProperties.Add(PropertyName);
}
if(ExposedPropertyNames.Contains(PropertyName) && FBlueprintEditorUtils::PropertyStillExists(Property))
{
FEdGraphPinType PinType;
verify(Schema->ConvertPropertyToPinType(Property, PinType));
UEdGraphPin* NewPin = CreatePin(EEdGraphPinDirection::EGPD_Input, PinType, Property->GetFName());
NewPin->PinFriendlyName = Property->GetDisplayNameText();
// We cant interrogate CDO here as we may be mid-compile, so we can only really
// reset to the autogenerated default.
AnimGraphDefaultSchema->ResetPinToAutogeneratedDefaultValue(NewPin, false);
CustomizePinData(NewPin, PropertyName, INDEX_NONE);
}
}
// Remove any properties that no longer exist on the target class
for(FName& RemovedPropertyName : BeginExposableNames)
{
KnownExposableProperties.Remove(RemovedPropertyName);
}
}
void UAnimGraphNode_CustomProperty::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
Super::CustomizeDetails(DetailBuilder);
// We dont allow multi-select here
if(DetailBuilder.GetSelectedObjects().Num() > 1)
{
DetailBuilder.HideCategory(TEXT("Settings"));
return;
}
TArray<UProperty*> ExposableProperties;
GetExposableProperties(ExposableProperties);
if(ExposableProperties.Num() > 0)
{
IDetailCategoryBuilder& CategoryBuilder = DetailBuilder.EditCategory(FName(TEXT("Exposable Properties")));
FDetailWidgetRow& HeaderWidgetRow = CategoryBuilder.AddCustomRow(LOCTEXT("ExposeAll", "Expose All"));
HeaderWidgetRow.NameContent()
[
SNew(STextBlock)
.Text(LOCTEXT("PropertyName", "Name"))
.Font(IDetailLayoutBuilder::GetDetailFontBold())
];
HeaderWidgetRow.ValueContent()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("ExposeAllPropertyValue", "Expose All"))
.Font(IDetailLayoutBuilder::GetDetailFontBold())
]
+SHorizontalBox::Slot()
.FillWidth(1.0f)
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
[
SNew(SCheckBox)
.IsChecked_UObject(this, &UAnimGraphNode_CustomProperty::AreAllPropertiesExposed)
.OnCheckStateChanged_UObject(this, &UAnimGraphNode_CustomProperty::OnPropertyExposeAllCheckboxChanged)
]
];
for(UProperty* Property : ExposableProperties)
{
FDetailWidgetRow& PropertyWidgetRow = CategoryBuilder.AddCustomRow(FText::FromString(Property->GetName()));
FName PropertyName = Property->GetFName();
FText PropertyTypeText = GetPropertyTypeText(Property);
FFormatNamedArguments Args;
Args.Add(TEXT("PropertyName"), FText::FromName(PropertyName));
Args.Add(TEXT("PropertyType"), PropertyTypeText);
FText TooltipText = FText::Format(LOCTEXT("PropertyTooltipText", "{PropertyName}\nType: {PropertyType}"), Args);
PropertyWidgetRow.NameContent()
[
SNew(STextBlock)
.Text(FText::FromString(Property->GetName()))
.ToolTipText(TooltipText)
.Font(IDetailLayoutBuilder::GetDetailFont())
];
PropertyWidgetRow.ValueContent()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.AutoWidth()
[
SNew(STextBlock)
.Text(LOCTEXT("ExposePropertyValue", "Expose:"))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
+SHorizontalBox::Slot()
.FillWidth(1.0f)
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
[
SNew(SCheckBox)
.IsChecked_UObject(this, &UAnimGraphNode_CustomProperty::IsPropertyExposed, PropertyName)
.OnCheckStateChanged_UObject(this, &UAnimGraphNode_CustomProperty::OnPropertyExposeCheckboxChanged, PropertyName)
]
];
}
}
IDetailCategoryBuilder& CategoryBuilder = DetailBuilder.EditCategory(FName(TEXT("Settings")));
// Customize InstanceClass
{
TSharedRef<IPropertyHandle> ClassHandle = DetailBuilder.GetProperty(TEXT("Node.InstanceClass"), GetClass());
if(ClassHandle->IsValidHandle())
{
ClassHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateUObject(this, &UAnimGraphNode_CustomProperty::OnStructuralPropertyChanged, &DetailBuilder));
}
}
}
void UAnimGraphNode_CustomProperty::GetInstancePinProperty(const UClass* InOwnerInstanceClass, UEdGraphPin* InInputPin, UProperty*& OutProperty)
{
// The actual name of the instance property
FString FullName = GetPinTargetVariableName(InInputPin);
if(UProperty* Property = FindField<UProperty>(InOwnerInstanceClass, *FullName))
{
OutProperty = Property;
}
else
{
OutProperty = nullptr;
}
}
FString UAnimGraphNode_CustomProperty::GetPinTargetVariableName(const UEdGraphPin* InPin) const
{
return TEXT("__CustomProperty_") + InPin->PinName.ToString() + TEXT("_") + NodeGuid.ToString();
}
FText UAnimGraphNode_CustomProperty::GetPropertyTypeText(UProperty* Property)
{
FText PropertyTypeText;
if(UStructProperty* StructProperty = Cast<UStructProperty>(Property))
{
PropertyTypeText = StructProperty->Struct->GetDisplayNameText();
}
else if(UObjectProperty* ObjectProperty = Cast<UObjectProperty>(Property))
{
PropertyTypeText = ObjectProperty->PropertyClass->GetDisplayNameText();
}
else if(UClass* PropClass = Property->GetClass())
{
PropertyTypeText = PropClass->GetDisplayNameText();
}
else
{
PropertyTypeText = LOCTEXT("PropertyTypeUnknown", "Unknown");
}
return PropertyTypeText;
}
void UAnimGraphNode_CustomProperty::RebuildExposedProperties()
{
ExposedPropertyNames.Empty();
KnownExposableProperties.Empty();
TArray<UProperty*> ExposableProperties;
GetExposableProperties(ExposableProperties);
for(UProperty* Property : ExposableProperties)
{
KnownExposableProperties.Add(Property->GetFName());
}
}
ECheckBoxState UAnimGraphNode_CustomProperty::AreAllPropertiesExposed() const
{
if(ExposedPropertyNames.Num() == 0)
{
return ECheckBoxState::Unchecked;
}
else
{
for(FName PropertyName : KnownExposableProperties)
{
if(!ExposedPropertyNames.Contains(PropertyName))
{
return ECheckBoxState::Undetermined;
}
}
}
return ECheckBoxState::Checked;
}
void UAnimGraphNode_CustomProperty::OnPropertyExposeAllCheckboxChanged(ECheckBoxState NewState)
{
if(NewState == ECheckBoxState::Checked)
{
ExposedPropertyNames = KnownExposableProperties;
}
else
{
ExposedPropertyNames.Empty();
}
ReconstructNode();
}
ECheckBoxState UAnimGraphNode_CustomProperty::IsPropertyExposed(FName PropertyName) const
{
return ExposedPropertyNames.Contains(PropertyName) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
void UAnimGraphNode_CustomProperty::OnPropertyExposeCheckboxChanged(ECheckBoxState NewState, FName PropertyName)
{
if(NewState == ECheckBoxState::Checked)
{
ExposedPropertyNames.AddUnique(PropertyName);
}
else if(NewState == ECheckBoxState::Unchecked)
{
ExposedPropertyNames.Remove(PropertyName);
}
ReconstructNode();
}
void UAnimGraphNode_CustomProperty::OnInstanceClassChanged(IDetailLayoutBuilder* DetailBuilder)
{
if(DetailBuilder)
{
DetailBuilder->ForceRefreshDetails();
}
}
UObject* UAnimGraphNode_CustomProperty::GetJumpTargetForDoubleClick() const
{
UClass* InstanceClass = GetTargetClass();
if(InstanceClass)
{
return InstanceClass->ClassGeneratedBy;
}
return nullptr;
}
bool UAnimGraphNode_CustomProperty::HasExternalDependencies(TArray<class UStruct*>* OptionalOutput /*= NULL*/) const
{
UClass* InstanceClassToUse = GetTargetClass();
// Add our instance class... If that changes we need a recompile
if(InstanceClassToUse && OptionalOutput)
{
OptionalOutput->AddUnique(InstanceClassToUse);
}
bool bSuperResult = Super::HasExternalDependencies(OptionalOutput);
return InstanceClassToUse || bSuperResult;
}
void UAnimGraphNode_CustomProperty::GetExposableProperties( TArray<UProperty*>& OutExposableProperties) const
{
OutExposableProperties.Empty();
UClass* TargetClass = GetTargetClass();
if(TargetClass)
{
const UEdGraphSchema_K2* Schema = CastChecked<UEdGraphSchema_K2>(GetSchema());
for(TFieldIterator<UProperty> It(TargetClass, EFieldIteratorFlags::IncludeSuper); It; ++It)
{
UProperty* CurProperty = *It;
FEdGraphPinType PinType;
if(CurProperty->HasAllPropertyFlags(CPF_Edit | CPF_BlueprintVisible) && CurProperty->HasAllFlags(RF_Public) && Schema->ConvertPropertyToPinType(CurProperty, PinType))
{
OutExposableProperties.Add(CurProperty);
}
}
}
}
void UAnimGraphNode_CustomProperty::AddSourceTargetProperties(const FName& InSourcePropertyName, const FName& InTargetPropertyName)
{
FAnimNode_CustomProperty* CustomPropAnimNode = GetCustomPropertyNode();
if (CustomPropAnimNode)
{
CustomPropAnimNode->SourcePropertyNames.Add(InSourcePropertyName);
CustomPropAnimNode->DestPropertyNames.Add(InTargetPropertyName);
}
}
UClass* UAnimGraphNode_CustomProperty::GetTargetClass() const
{
const FAnimNode_CustomProperty* CustomPropAnimNode = GetCustomPropertyNode();
if (CustomPropAnimNode)
{
return CustomPropAnimNode->GetTargetClass();
}
return nullptr;
}
UClass* UAnimGraphNode_CustomProperty::GetTargetSkeletonClass() const
{
UClass* TargetClass = GetTargetClass();
if(TargetClass)
{
UBlueprint* Blueprint = CastChecked<UBlueprint>(TargetClass->ClassGeneratedBy);
if(Blueprint)
{
if (Blueprint->SkeletonGeneratedClass)
{
return Blueprint->SkeletonGeneratedClass;
}
}
}
return TargetClass;
}
void UAnimGraphNode_CustomProperty::OnStructuralPropertyChanged(IDetailLayoutBuilder* DetailBuilder)
{
if(DetailBuilder)
{
DetailBuilder->ForceRefreshDetails();
}
}
#undef LOCTEXT_NAMESPACE