diff --git a/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Rigs/RigHierarchyDefines.h b/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Rigs/RigHierarchyDefines.h index 95064384bcd5..3bff5912407f 100644 --- a/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Rigs/RigHierarchyDefines.h +++ b/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Rigs/RigHierarchyDefines.h @@ -17,7 +17,7 @@ class URigHierarchy; * This is rig element types that we support * This can be used as a mask so supported as a bitfield */ -UENUM(BlueprintType) +UENUM(BlueprintType, meta = (RigVMTypeAllowed)) enum class ERigElementType : uint8 { None = 0, @@ -47,7 +47,7 @@ enum class ERigBoneType : uint8 /* * The type of meta data stored on an element */ -UENUM(BlueprintType) +UENUM(BlueprintType, meta = (RigVMTypeAllowed)) enum class ERigMetadataType : uint8 { Bool, @@ -103,7 +103,7 @@ enum class ERigHierarchyNotification : uint8 Max UMETA(Hidden), }; -UENUM() +UENUM(meta = (RigVMTypeAllowed)) enum class ERigEvent : uint8 { /** Invalid event */ @@ -131,7 +131,7 @@ enum class EControlRigSetKey : uint8 Never //Never set a key here. }; -UENUM(BlueprintType) +UENUM(BlueprintType, meta = (RigVMTypeAllowed)) enum class ERigControlType : uint8 { Bool, @@ -171,7 +171,7 @@ enum class ERigControlValueType : uint8 Maximum }; -UENUM(BlueprintType) +UENUM(BlueprintType, meta = (RigVMTypeAllowed)) enum class ERigControlVisibility : uint8 { // Visibility controlled by the graph @@ -180,7 +180,7 @@ enum class ERigControlVisibility : uint8 BasedOnSelection }; -UENUM(BlueprintType) +UENUM(BlueprintType, meta = (RigVMTypeAllowed)) enum class ERigControlAxis : uint8 { X, diff --git a/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Units/Execution/RigUnit_DynamicHierarchy.h b/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Units/Execution/RigUnit_DynamicHierarchy.h index e83029c94396..f585d0ddd917 100644 --- a/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Units/Execution/RigUnit_DynamicHierarchy.h +++ b/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Units/Execution/RigUnit_DynamicHierarchy.h @@ -82,7 +82,7 @@ struct CONTROLRIG_API FRigUnit_SetDefaultParent : public FRigUnit_DynamicHierarc FRigElementKey Parent; }; -UENUM() +UENUM(meta = (RigVMTypeAllowed)) enum class ERigSwitchParentMode : uint8 { /** Switches the element to be parented to the world */ diff --git a/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Units/RigUnitContext.h b/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Units/RigUnitContext.h index 8186287da381..96a313b93338 100644 --- a/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Units/RigUnitContext.h +++ b/Engine/Plugins/Animation/ControlRig/Source/ControlRig/Public/Units/RigUnitContext.h @@ -28,7 +28,7 @@ enum class EControlRigInteractionType : uint8 All = Translate | Rotate | Scale }; -UENUM(BlueprintType) +UENUM(BlueprintType, meta = (RigVMTypeAllowed)) enum class ERigMetaDataNameSpace : uint8 { // Use no namespace - store the metadata directly on the item diff --git a/Engine/Plugins/Animation/ControlRig/Source/ControlRigEditor/Private/ControlRigElementDetails.cpp b/Engine/Plugins/Animation/ControlRig/Source/ControlRigEditor/Private/ControlRigElementDetails.cpp index feb91fd7cafb..7a8dd2264ae4 100644 --- a/Engine/Plugins/Animation/ControlRig/Source/ControlRigEditor/Private/ControlRigElementDetails.cpp +++ b/Engine/Plugins/Animation/ControlRig/Source/ControlRigEditor/Private/ControlRigElementDetails.cpp @@ -25,6 +25,7 @@ #include "Editor/SRigHierarchyTreeView.h" #include "StructViewerFilter.h" #include "StructViewerModule.h" +#include "Widgets/SRigVMGraphPinEnumPicker.h" #define LOCTEXT_NAMESPACE "ControlRigElementDetails" @@ -3186,60 +3187,43 @@ void FRigControlElementDetails::CustomizeControl(IDetailLayoutBuilder& DetailBui } if(IsAnyControlOfValueType(ERigControlType::Integer)) - { - const TSharedPtr ControlEnumHandle = SettingsHandle->GetChildHandle(TEXT("ControlEnum")); - ControlCategory.AddProperty(ControlEnumHandle.ToSharedRef()).DisplayName(FText::FromString(TEXT("Control Enum"))) - .IsEnabled(bIsEnabled); - - ControlEnumHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda( - [this, PropertyUtilities]() + { + FDetailWidgetRow* EnumWidgetRow = &ControlCategory.AddCustomRow(FText::FromString(TEXT("ControlEnum"))) + .NameContent() + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("Control Enum"))) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .IsEnabled(bIsEnabled) + ] + .ValueContent() + [ + SNew(SRigVMEnumPicker) + .OnEnumChanged(this, &FRigControlElementDetails::HandleControlEnumChanged, PropertyUtilities) + .IsEnabled(bIsEnabled) + .GetCurrentEnum_Lambda([this]() { - PropertyUtilities->ForceRefresh(); - - for(int32 ControlIndex = 0; ControlIndex < PerElementInfos.Num(); ControlIndex++) + UEnum* CommonControlEnum = nullptr; + for (int32 ControlIndex=0; ControlIndex < PerElementInfos.Num(); ++ControlIndex) { FPerElementInfo& Info = PerElementInfos[ControlIndex]; const FRigControlElement ControlInView = Info.WrapperObject->GetContent(); FRigControlElement* ControlBeingCustomized = Info.GetDefaultElement(); - - const UEnum* ControlEnum = ControlBeingCustomized->Settings.ControlEnum; - if (ControlEnum != nullptr) + + UEnum* ControlEnum = ControlBeingCustomized->Settings.ControlEnum; + if (ControlIndex == 0) { - int32 Maximum = (int32)ControlEnum->GetMaxEnumValue() - 1; - ControlBeingCustomized->Settings.MinimumValue.Set(0); - ControlBeingCustomized->Settings.MaximumValue.Set(Maximum); - ControlBeingCustomized->Settings.LimitEnabled.Reset(); - ControlBeingCustomized->Settings.LimitEnabled.Add(true); - Info.GetDefaultHierarchy()->SetControlSettings(ControlBeingCustomized, ControlBeingCustomized->Settings, true, true, true); - - FRigControlValue InitialValue = Info.GetDefaultHierarchy()->GetControlValue(ControlBeingCustomized, ERigControlValueType::Initial); - FRigControlValue CurrentValue = Info.GetDefaultHierarchy()->GetControlValue(ControlBeingCustomized, ERigControlValueType::Current); - - ControlBeingCustomized->Settings.ApplyLimits(InitialValue); - ControlBeingCustomized->Settings.ApplyLimits(CurrentValue); - Info.GetDefaultHierarchy()->SetControlValue(ControlBeingCustomized, InitialValue, ERigControlValueType::Initial, false, false, true); - Info.GetDefaultHierarchy()->SetControlValue(ControlBeingCustomized, CurrentValue, ERigControlValueType::Current, false, false, true); - - if (UControlRig* DebuggedRig = Cast(Info.GetBlueprint()->GetObjectBeingDebugged())) - { - URigHierarchy* DebuggedHierarchy = DebuggedRig->GetHierarchy(); - if(FRigControlElement* DebuggedControlElement = DebuggedHierarchy->Find(ControlBeingCustomized->GetKey())) - { - DebuggedControlElement->Settings.MinimumValue.Set(0); - DebuggedControlElement->Settings.MaximumValue.Set(Maximum); - DebuggedHierarchy->SetControlSettings(DebuggedControlElement, DebuggedControlElement->Settings, true, true, true); - - DebuggedHierarchy->SetControlValue(DebuggedControlElement, InitialValue, ERigControlValueType::Initial); - DebuggedHierarchy->SetControlValue(DebuggedControlElement, CurrentValue, ERigControlValueType::Current); - } - } + CommonControlEnum = ControlEnum; + } + else if(ControlEnum != CommonControlEnum) + { + CommonControlEnum = nullptr; + break; } - - Info.WrapperObject->SetContent(*ControlBeingCustomized); } - } - )); - + return CommonControlEnum; + }) + ]; } const TSharedPtr CustomizationHandle = SettingsHandle->GetChildHandle(TEXT("Customization")); @@ -3304,6 +3288,54 @@ void FRigControlElementDetails::CustomizeControl(IDetailLayoutBuilder& DetailBui } } +void FRigControlElementDetails::HandleControlEnumChanged(TSharedPtr InItem, ESelectInfo::Type InSelectionInfo, const TSharedRef PropertyUtilities) +{ + PropertyUtilities->ForceRefresh(); + UEnum* ControlEnum = FindObject(nullptr, **InItem.Get(), false); + + for(int32 ControlIndex = 0; ControlIndex < PerElementInfos.Num(); ControlIndex++) + { + FPerElementInfo& Info = PerElementInfos[ControlIndex]; + const FRigControlElement ControlInView = Info.WrapperObject->GetContent(); + FRigControlElement* ControlBeingCustomized = Info.GetDefaultElement(); + + ControlBeingCustomized->Settings.ControlEnum = ControlEnum; + if (ControlEnum != nullptr) + { + int32 Maximum = (int32)ControlEnum->GetMaxEnumValue() - 1; + ControlBeingCustomized->Settings.MinimumValue.Set(0); + ControlBeingCustomized->Settings.MaximumValue.Set(Maximum); + ControlBeingCustomized->Settings.LimitEnabled.Reset(); + ControlBeingCustomized->Settings.LimitEnabled.Add(true); + Info.GetDefaultHierarchy()->SetControlSettings(ControlBeingCustomized, ControlBeingCustomized->Settings, true, true, true); + + FRigControlValue InitialValue = Info.GetDefaultHierarchy()->GetControlValue(ControlBeingCustomized, ERigControlValueType::Initial); + FRigControlValue CurrentValue = Info.GetDefaultHierarchy()->GetControlValue(ControlBeingCustomized, ERigControlValueType::Current); + + ControlBeingCustomized->Settings.ApplyLimits(InitialValue); + ControlBeingCustomized->Settings.ApplyLimits(CurrentValue); + Info.GetDefaultHierarchy()->SetControlValue(ControlBeingCustomized, InitialValue, ERigControlValueType::Initial, false, false, true); + Info.GetDefaultHierarchy()->SetControlValue(ControlBeingCustomized, CurrentValue, ERigControlValueType::Current, false, false, true); + + if (UControlRig* DebuggedRig = Cast(Info.GetBlueprint()->GetObjectBeingDebugged())) + { + URigHierarchy* DebuggedHierarchy = DebuggedRig->GetHierarchy(); + if(FRigControlElement* DebuggedControlElement = DebuggedHierarchy->Find(ControlBeingCustomized->GetKey())) + { + DebuggedControlElement->Settings.MinimumValue.Set(0); + DebuggedControlElement->Settings.MaximumValue.Set(Maximum); + DebuggedHierarchy->SetControlSettings(DebuggedControlElement, DebuggedControlElement->Settings, true, true, true); + + DebuggedHierarchy->SetControlValue(DebuggedControlElement, InitialValue, ERigControlValueType::Initial); + DebuggedHierarchy->SetControlValue(DebuggedControlElement, CurrentValue, ERigControlValueType::Current); + } + } + } + + Info.WrapperObject->SetContent(*ControlBeingCustomized); + } +} + void FRigControlElementDetails::CustomizeAnimationChannels(IDetailLayoutBuilder& DetailBuilder) { // We only show this section for parents of animation channels diff --git a/Engine/Plugins/Animation/ControlRig/Source/ControlRigEditor/Private/ControlRigElementDetails.h b/Engine/Plugins/Animation/ControlRig/Source/ControlRigEditor/Private/ControlRigElementDetails.h index beba7de31fbb..782024f0b5e7 100644 --- a/Engine/Plugins/Animation/ControlRig/Source/ControlRigEditor/Private/ControlRigElementDetails.h +++ b/Engine/Plugins/Animation/ControlRig/Source/ControlRigEditor/Private/ControlRigElementDetails.h @@ -743,6 +743,7 @@ private: TSharedRef HandleGenerateAnimationChannelTypeRow(TSharedPtr ControlType, const TSharedRef& OwnerTable, FRigElementKey ControlKey); void HandleControlTypeChanged(TSharedPtr ControlType, ESelectInfo::Type SelectInfo, FRigElementKey ControlKey, const TSharedRef PropertyUtilities); void HandleControlTypeChanged(ERigControlType ControlType, TArray ControlKeys, const TSharedRef PropertyUtilities); + void HandleControlEnumChanged(TSharedPtr InItem, ESelectInfo::Type InSelectionInfo, const TSharedRef PropertyUtilities); TArray> ShapeNameList; TSharedPtr InfluenceModifier; diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_DebugPoint.h b/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_DebugPoint.h index 47d06144eb7e..ac78aa98881b 100644 --- a/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_DebugPoint.h +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_DebugPoint.h @@ -5,7 +5,7 @@ #include "RigVMFunction_DebugBase.h" #include "RigVMFunction_DebugPoint.generated.h" -UENUM() +UENUM(meta = (RigVMTypeAllowed)) enum class ERigUnitDebugPointMode : uint8 { /** Draw as point */ diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_DebugTransform.h b/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_DebugTransform.h index 925a682776b2..4ac6a8c73577 100644 --- a/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_DebugTransform.h +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_DebugTransform.h @@ -5,7 +5,7 @@ #include "RigVMFunction_DebugBase.h" #include "RigVMFunction_DebugTransform.generated.h" -UENUM() +UENUM(meta = (RigVMTypeAllowed)) enum class ERigUnitDebugTransformMode : uint8 { /** Draw as point */ diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_VisualDebug.h b/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_VisualDebug.h index c4385d490717..b3716b3b7137 100644 --- a/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_VisualDebug.h +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Debug/RigVMFunction_VisualDebug.h @@ -5,7 +5,7 @@ #include "RigVMFunction_DebugBase.h" #include "RigVMFunction_VisualDebug.generated.h" -UENUM() +UENUM(meta = (RigVMTypeAllowed)) enum class ERigUnitVisualDebugPointMode : uint8 { /** Draw as point */ diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Math/RigVMMathLibrary.h b/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Math/RigVMMathLibrary.h index 20df1e29d824..d29087217f54 100644 --- a/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Math/RigVMMathLibrary.h +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Math/RigVMMathLibrary.h @@ -8,7 +8,7 @@ #include "RigVMFunctions/RigVMFunctionDefines.h" #include "RigVMMathLibrary.generated.h" -UENUM() +UENUM(meta = (RigVMTypeAllowed)) enum class ERigVMAnimEasingType : uint8 { Linear, @@ -98,7 +98,7 @@ struct RIGVM_API FRigVMMirrorSettings FVector MirrorVector(const FVector& InVector) const; }; -UENUM() +UENUM(meta = (RigVMTypeAllowed)) enum class ERigVMSimPointIntegrateType : uint8 { Verlet, diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/RigVMFunctionDefines.h b/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/RigVMFunctionDefines.h index 2ba2b446520a..9ce55d20a56f 100644 --- a/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/RigVMFunctionDefines.h +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/RigVMFunctionDefines.h @@ -7,7 +7,7 @@ #include "RigVMFunctionDefines.generated.h" -UENUM() +UENUM(meta = (RigVMTypeAllowed)) enum class ERigVMTransformSpace : uint8 { /** Apply in parent space */ @@ -20,7 +20,7 @@ enum class ERigVMTransformSpace : uint8 Max UMETA(Hidden), }; -UENUM() +UENUM(meta = (RigVMTypeAllowed)) namespace ERigVMClampSpatialMode { enum Type : int diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/EdGraph/RigVMEdGraphPanelPinFactory.cpp b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/EdGraph/RigVMEdGraphPanelPinFactory.cpp index 15b4c17f542b..2b196aac3837 100644 --- a/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/EdGraph/RigVMEdGraphPanelPinFactory.cpp +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/EdGraph/RigVMEdGraphPanelPinFactory.cpp @@ -19,6 +19,7 @@ #include "Curves/CurveFloat.h" #include "RigVMModel/Nodes/RigVMUnitNode.h" #include "RigVMCore/RigVMExecuteContext.h" +#include "Widgets/SRigVMGraphPinEnumPicker.h" FName FRigVMEdGraphPanelPinFactory::GetFactoryName() const { @@ -113,6 +114,12 @@ TSharedPtr FRigVMEdGraphPanelPinFactory::CreatePin_Internal(UEdGraphP return SNew(SRigVMGraphPinUserDataPath, InPin) .ModelPins({ModelPin}); } + + if (ModelPin->GetCPPTypeObject() == UEnum::StaticClass()) + { + return SNew(SRigVMGraphPinEnumPicker, InPin) + .ModelPin(ModelPin); + } } if (InPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct) diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Editor/RigVMEditor.cpp b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Editor/RigVMEditor.cpp index bba78ff88e86..091659b92373 100644 --- a/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Editor/RigVMEditor.cpp +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Editor/RigVMEditor.cpp @@ -372,6 +372,12 @@ void FRigVMEditor::InitRigVMEditor(const EToolkitMode::Type Mode, const TSharedP })); } + Inspector->GetPropertyView()->RegisterInstancedCustomPropertyTypeLayout(UEnum::StaticClass()->GetFName(), + FOnGetPropertyTypeCustomizationInstance::CreateLambda([=]() + { + return FRigVMGraphEnumDetailCustomization::MakeInstance(); + })); + PropertyChangedHandle = FCoreUObjectDelegates::OnObjectPropertyChanged.AddSP(this, &FRigVMEditor::OnPropertyChanged); } diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Editor/RigVMGraphDetailCustomization.cpp b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Editor/RigVMGraphDetailCustomization.cpp index 2b0b0da38c0b..b0929b1f5968 100644 --- a/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Editor/RigVMGraphDetailCustomization.cpp +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Editor/RigVMGraphDetailCustomization.cpp @@ -26,6 +26,7 @@ #include "RigVMModel/Nodes/RigVMAggregateNode.h" #include "Widgets/SRigVMGraphPinVariableBinding.h" #include "InstancedPropertyBagStructureDataProvider.h" +#include "Widgets/SRigVMGraphPinEnumPicker.h" #define LOCTEXT_NAMESPACE "RigVMGraphDetailCustomization" @@ -1834,6 +1835,148 @@ void FRigVMWrappedNodeDetailCustomization::CustomizeLiveValues(IDetailLayoutBuil */ } +FRigVMGraphEnumDetailCustomization::FRigVMGraphEnumDetailCustomization() +: BlueprintBeingCustomized(nullptr) +, GraphBeingCustomized(nullptr) +{ +} + +void FRigVMGraphEnumDetailCustomization::CustomizeHeader(TSharedRef InPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + TArray Objects; + InPropertyHandle->GetOuterObjects(Objects); + + StructsBeingCustomized.Reset(); + InPropertyHandle->GetOuterStructs(StructsBeingCustomized); + + for (UObject* Object : Objects) + { + ObjectsBeingCustomized.Add(Object); + + if(BlueprintBeingCustomized == nullptr) + { + BlueprintBeingCustomized = Object->GetTypedOuter(); + } + + if(GraphBeingCustomized == nullptr) + { + GraphBeingCustomized = Object->GetTypedOuter(); + } + } + + FProperty* Property = InPropertyHandle->GetProperty(); + const FObjectProperty* ObjectProperty = CastField(Property); + + HeaderRow + .NameContent() + [ + InPropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + .MinDesiredWidth(375.f) + .MaxDesiredWidth(375.f) + .HAlign(HAlign_Left) + [ + SNew(SBox) + .MinDesiredWidth(150) + .MaxDesiredWidth(400) + [ + SNew(SRigVMEnumPicker) + .IsEnabled(true) + .OnEnumChanged(this, &FRigVMGraphEnumDetailCustomization::HandleControlEnumChanged, InPropertyHandle) + .GetCurrentEnum_Lambda([this, InPropertyHandle]() + { + UEnum* Enum = nullptr; + FEditPropertyChain PropertyChain; + TArray PropertyArrayIndices; + bool bEnabled; + if (!GetPropertyChain(InPropertyHandle, PropertyChain, PropertyArrayIndices, bEnabled)) + { + return Enum; + } + + const TArray MemoryBlocks = GetMemoryBeingCustomized(); + for(uint8* MemoryBlock: MemoryBlocks) + { + if(MemoryBlock) + { + if (UEnum** CurrentEnum = ContainerMemoryBlockToEnumPtr(MemoryBlock, PropertyChain, PropertyArrayIndices)) + { + Enum = *CurrentEnum; + } + } + } + return Enum; + }) + ] + ]; +} + +void FRigVMGraphEnumDetailCustomization::CustomizeChildren(TSharedRef InPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + // nothing to do here +} + +void FRigVMGraphEnumDetailCustomization::HandleControlEnumChanged(TSharedPtr InEnumPath, ESelectInfo::Type InSelectType, TSharedRef InPropertyHandle) +{ + if (ObjectsBeingCustomized.IsEmpty()) + { + return; + } + + FEditPropertyChain PropertyChain; + TArray PropertyArrayIndices; + bool bEnabled; + if (!GetPropertyChain(InPropertyHandle, PropertyChain, PropertyArrayIndices, bEnabled)) + { + return; + } + + TArray ObjectsView; + for(int32 Index = 0; Index < ObjectsBeingCustomized.Num(); Index++) + { + const TWeakObjectPtr& Object = ObjectsBeingCustomized[Index]; + if (Object.Get()) + { + ObjectsView.Add(Object.Get()); + } + } + FPropertyChangedEvent PropertyChangedEvent(InPropertyHandle->GetProperty(), EPropertyChangeType::ValueSet, ObjectsView); + FPropertyChangedChainEvent PropertyChangedChainEvent(PropertyChain, PropertyChangedEvent); + + URigVMController* Controller = nullptr; + if(BlueprintBeingCustomized && GraphBeingCustomized) + { + Controller = BlueprintBeingCustomized->GetController(GraphBeingCustomized); + Controller->OpenUndoBracket(FString::Printf(TEXT("Set %s"), *InPropertyHandle->GetProperty()->GetName())); + } + + for(int32 Index = 0; Index < ObjectsBeingCustomized.Num(); Index++) + { + const TWeakObjectPtr& Object = ObjectsBeingCustomized[Index]; + if(Object.Get() && InPropertyHandle->IsValidHandle()) + { + UEnum** CurrentEnum = ContainerMemoryBlockToEnumPtr((uint8*)Object.Get(), PropertyChain, PropertyArrayIndices); + if (CurrentEnum) + { + const UEnum* PreviousEnum = *CurrentEnum; + *CurrentEnum = FindObject(nullptr, **InEnumPath.Get(), false); + + if (PreviousEnum != *CurrentEnum) + { + Object->PostEditChangeChainProperty(PropertyChangedChainEvent); + InPropertyHandle->NotifyPostChange(PropertyChangedEvent.ChangeType); + } + } + } + } + + if(Controller) + { + Controller->CloseUndoBracket(); + } +} + template void FRigVMGraphMathTypeDetailCustomization::MakeVectorHeaderRow(TSharedRef InPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Widgets/SRigVMGraphPinEnumPicker.cpp b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Widgets/SRigVMGraphPinEnumPicker.cpp new file mode 100644 index 000000000000..87ee468ac48e --- /dev/null +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Private/Widgets/SRigVMGraphPinEnumPicker.cpp @@ -0,0 +1,133 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + + +#include "Widgets/SRigVMGraphPinEnumPicker.h" + +#include "DetailLayoutBuilder.h" +#include "EdGraph/RigVMEdGraph.h" +#include "RigVMBlueprint.h" +#include "Widgets/Layout/SBox.h" +#include "ScopedTransaction.h" +#include "SSearchableComboBox.h" +#include "UObject/UObjectIterator.h" + +void SRigVMEnumPicker::Construct(const FArguments& InArgs) +{ + OnEnumChangedDelegate = InArgs._OnEnumChanged; + bIsEnabled = InArgs._IsEnabled; + GetCurrentEnumDelegate = InArgs._GetCurrentEnum; + + PopulateEnumOptions(); + + ChildSlot + [ + SNew(SBox) + .MinDesiredWidth(150) + .MaxDesiredWidth(400) + [ + SNew(SSearchableComboBox) + .OptionsSource(&EnumOptions) + .OnSelectionChanged(this, &SRigVMEnumPicker::HandleControlEnumChanged) + .OnGenerateWidget(this, &SRigVMEnumPicker::OnGetEnumNameWidget) + .Content() + [ + SNew(STextBlock) + .Text_Lambda([this]() -> FText + { + const UEnum* ControlEnum = GetCurrentEnum(); + if (ControlEnum) + { + return FText::FromName(ControlEnum->GetFName()); + } + return FText(); + }) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + ] + ]; +} + +TSharedRef SRigVMEnumPicker::OnGetEnumNameWidget(TSharedPtr InItem) +{ + if (InItem.IsValid()) + { + UEnum* Enum = FindObject(nullptr, **InItem.Get(), false); + if (Enum) + { + return SNew(STextBlock) + .Text(FText::FromString(Enum->GetName())) + .ToolTip(FSlateApplication::Get().MakeToolTip(FText::FromString(Enum->GetPathName()))) + .Font(IDetailLayoutBuilder::GetDetailFont()); + } + } + return SNew(STextBlock) + .Text(FText::FromString(TEXT("None"))) + .Font(IDetailLayoutBuilder::GetDetailFont()); +} + +void SRigVMEnumPicker::PopulateEnumOptions() +{ + EnumOptions.Reset(); + EnumOptions.Add(MakeShareable(new FString(TEXT("None")))); + for (TObjectIterator EnumIt; EnumIt; ++EnumIt) + { + UEnum* Enum = *EnumIt; + + if (Enum->HasAnyFlags(RF_BeginDestroyed | RF_FinishDestroyed) || !Enum->HasAllFlags(RF_Public)) + { + continue; + } + + // Any asset based enum is valid + if (!Enum->IsAsset()) + { + // Native enums only allowed if contain RigVMTypeAllowed metadata + if (!Enum->HasMetaData(TEXT("RigVMTypeAllowed"))) + { + continue; + } + } + + EnumOptions.Add(MakeShareable(new FString(Enum->GetPathName()))); + } +} + +void SRigVMGraphPinEnumPicker::Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj) +{ + ModelPin = InArgs._ModelPin; + SGraphPin::Construct(SGraphPin::FArguments(), InGraphPinObj); +} + +TSharedRef SRigVMGraphPinEnumPicker::GetDefaultValueWidget() +{ + return SNew(SRigVMEnumPicker) + .GetCurrentEnum_Lambda([this]() + { + UEnum* ControlEnum = nullptr; + if (ModelPin) + { + const FString& Path = ModelPin->GetDefaultValue(); + ControlEnum = FindObject(nullptr, *Path, false); + } + return ControlEnum; + }) + .OnEnumChanged(this, &SRigVMGraphPinEnumPicker::HandleControlEnumChanged); +} + +void SRigVMGraphPinEnumPicker::HandleControlEnumChanged(TSharedPtr InItem, ESelectInfo::Type InSelectionInfo) +{ + if(ModelPin) + { + if(URigVMBlueprint* Blueprint = ModelPin->GetTypedOuter()) + { + if(URigVMController* Controller = Blueprint->GetOrCreateController(ModelPin->GetGraph())) + { + Controller->SetPinDefaultValue(ModelPin->GetPinPath(), *InItem.Get()); + } + } + } +} + + + + diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Public/Editor/RigVMGraphDetailCustomization.h b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Public/Editor/RigVMGraphDetailCustomization.h index b3249cdf596d..6051b34b3896 100644 --- a/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Public/Editor/RigVMGraphDetailCustomization.h +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Public/Editor/RigVMGraphDetailCustomization.h @@ -240,6 +240,135 @@ public: TMap> NameListWidgets; }; +/** Customization for editing a rig vm integer control enum class */ +class RIGVMEDITOR_API FRigVMGraphEnumDetailCustomization : public IPropertyTypeCustomization +{ +public: + + FRigVMGraphEnumDetailCustomization(); + + static TSharedRef MakeInstance() + { + return MakeShareable(new FRigVMGraphEnumDetailCustomization); + } + + /** IPropertyTypeCustomization interface */ + virtual void CustomizeHeader(TSharedRef InPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef InPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + +protected: + + TArray GetMemoryBeingCustomized() + { + TArray MemoryPtr; + MemoryPtr.Reserve(ObjectsBeingCustomized.Num() + StructsBeingCustomized.Num()); + + for(const TWeakObjectPtr& Object : ObjectsBeingCustomized) + { + if(Object.IsValid()) + { + MemoryPtr.Add((uint8*)Object.Get()); + } + } + + for(const TSharedPtr& StructPtr: StructsBeingCustomized) + { + if(StructPtr.IsValid()) + { + MemoryPtr.Add(StructPtr->GetStructMemory()); + } + } + + return MemoryPtr; + } + + bool GetPropertyChain(TSharedRef InPropertyHandle, FEditPropertyChain& OutPropertyChain, TArray &OutPropertyArrayIndices, bool& bOutEnabled) + { + if (!InPropertyHandle->IsValidHandle()) + { + return false; + } + + OutPropertyChain.Empty(); + OutPropertyArrayIndices.Reset(); + bOutEnabled = false; + + const bool bHasObject = !ObjectsBeingCustomized.IsEmpty() && ObjectsBeingCustomized[0].Get(); + const bool bHasStruct = !StructsBeingCustomized.IsEmpty() && StructsBeingCustomized[0].Get(); + + if (bHasStruct || bHasObject) + { + TSharedPtr ChainHandle = InPropertyHandle; + while (ChainHandle.IsValid() && ChainHandle->GetProperty() != nullptr) + { + OutPropertyChain.AddHead(ChainHandle->GetProperty()); + OutPropertyArrayIndices.Insert(ChainHandle->GetIndexInArray(), 0); + ChainHandle = ChainHandle->GetParentHandle(); + } + + if (OutPropertyChain.GetHead() != nullptr) + { + OutPropertyChain.SetActiveMemberPropertyNode(OutPropertyChain.GetTail()->GetValue()); + bOutEnabled = !OutPropertyChain.GetHead()->GetValue()->HasAnyPropertyFlags(CPF_EditConst); + return true; + } + } + return false; + } + + // extracts the value for a nested property from an outer owner + static UEnum** ContainerMemoryBlockToEnumPtr(uint8* InMemoryBlock, FEditPropertyChain& InPropertyChain, TArray &InPropertyArrayIndices) + { + if (InPropertyChain.GetHead() == nullptr) + { + return nullptr; + } + + FEditPropertyChain::TDoubleLinkedListNode* PropertyNode = InPropertyChain.GetHead(); + uint8* MemoryPtr = InMemoryBlock; + int32 ChainIndex = 0; + do + { + const FProperty* Property = PropertyNode->GetValue(); + MemoryPtr = Property->ContainerPtrToValuePtr(MemoryPtr); + + PropertyNode = PropertyNode->GetNextNode(); + ChainIndex++; + + if(InPropertyArrayIndices.IsValidIndex(ChainIndex)) + { + const int32 ArrayIndex = InPropertyArrayIndices[ChainIndex]; + if(ArrayIndex != INDEX_NONE) + { + const FArrayProperty* ArrayProperty = CastField(Property->GetOwnerProperty()); + check(ArrayProperty); + + FScriptArrayHelper ArrayHelper(ArrayProperty, MemoryPtr); + if(!ArrayHelper.IsValidIndex(ArrayIndex)) + { + return nullptr; + } + MemoryPtr = ArrayHelper.GetRawPtr(ArrayIndex); + + // skip to the next property node already + PropertyNode = PropertyNode->GetNextNode(); + ChainIndex++; + } + } + } + while (PropertyNode); + + return (UEnum**)MemoryPtr; + } + + void HandleControlEnumChanged(TSharedPtr InEnumPath, ESelectInfo::Type InSelectType, TSharedRef InPropertyHandle); + + URigVMBlueprint* BlueprintBeingCustomized; + URigVMGraph* GraphBeingCustomized; + TArray> ObjectsBeingCustomized; + TArray> StructsBeingCustomized; +}; + /** Customization for editing a rig vm node */ class RIGVMEDITOR_API FRigVMGraphMathTypeDetailCustomization : public IPropertyTypeCustomization { diff --git a/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Public/Widgets/SRigVMGraphPinEnumPicker.h b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Public/Widgets/SRigVMGraphPinEnumPicker.h new file mode 100644 index 000000000000..ae126fa4be06 --- /dev/null +++ b/Engine/Plugins/Runtime/RigVM/Source/RigVMEditor/Public/Widgets/SRigVMGraphPinEnumPicker.h @@ -0,0 +1,72 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "Widgets/SWidget.h" +#include "SGraphPin.h" +#include "SSearchableComboBox.h" +#include "RigVMModel/RigVMPin.h" + +class RIGVMEDITOR_API SRigVMEnumPicker : public SCompoundWidget +{ +public: + DECLARE_DELEGATE_RetVal(TObjectPtr, FGetCurrentEnum); + + SLATE_BEGIN_ARGS(SRigVMEnumPicker){} + SLATE_EVENT(SSearchableComboBox::FOnSelectionChanged, OnEnumChanged) + SLATE_EVENT(FGetCurrentEnum, GetCurrentEnum) + SLATE_ARGUMENT(bool, IsEnabled) + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs); + +protected: + + void HandleControlEnumChanged(TSharedPtr InItem, ESelectInfo::Type InSelectionInfo) + { + if (OnEnumChangedDelegate.IsBound()) + { + return OnEnumChangedDelegate.Execute(InItem, InSelectionInfo); + } + } + + UEnum* GetCurrentEnum() + { + if (GetCurrentEnumDelegate.IsBound()) + { + return GetCurrentEnumDelegate.Execute(); + } + return nullptr; + } + + TSharedRef OnGetEnumNameWidget(TSharedPtr InItem); + void PopulateEnumOptions(); + + TArray> EnumOptions; + SSearchableComboBox::FOnSelectionChanged OnEnumChangedDelegate; + FGetCurrentEnum GetCurrentEnumDelegate; + bool bIsEnabled; +}; + +class RIGVMEDITOR_API SRigVMGraphPinEnumPicker : public SGraphPin +{ +public: + SLATE_BEGIN_ARGS(SRigVMGraphPinEnumPicker) + : _ModelPin(nullptr) {} + SLATE_ARGUMENT(URigVMPin*, ModelPin) + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj); + +protected: + //~ Begin SGraphPin Interface + virtual TSharedRef GetDefaultValueWidget() override; + //~ End SGraphPin Interface + + void HandleControlEnumChanged(TSharedPtr InItem, ESelectInfo::Type InSelectionInfo); + + URigVMPin* ModelPin; + +};