Files
UnrealEngineUWP/Engine/Plugins/Animation/ControlRig/Source/ControlRigEditor/Private/ControlRigElementDetails.cpp
Helge Mathee 75556815e1 Control Rig: Support variable binding in details panel.
Also added support for local variables in binding.
+ React to removing local variable
+ React to renaming local variable
+ React to changing local variable type

#rb sara.schvartzman
#jira UE-101980

[CL 16703406 by Helge Mathee in ue5-main branch]
2021-06-17 08:21:34 -04:00

1920 lines
69 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ControlRigElementDetails.h"
#include "Widgets/SWidget.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
#include "IDetailChildrenBuilder.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Widgets/Input/SVectorInputBox.h"
#include "Widgets/Input/SRotatorInputBox.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Input/SNumericEntryBox.h"
#include "Widgets/Input/SComboBox.h"
#include "Widgets/Input/SButton.h"
#include "Editor/SControlRigGizmoNameList.h"
#include "ControlRigBlueprint.h"
#include "Graph/ControlRigGraph.h"
#include "PropertyCustomizationHelpers.h"
#include "SEnumCombo.h"
#include "ControlRig/Private/Units/Execution/RigUnit_BeginExecution.h"
#include "RigVMModel/RigVMGraph.h"
#include "RigVMModel/RigVMNode.h"
#include "Graph/SControlRigGraphPinVariableBinding.h"
#define LOCTEXT_NAMESPACE "ControlRigElementDetails"
namespace FRigElementKeyDetailsDefs
{
// Active foreground pin alpha
static const float ActivePinForegroundAlpha = 1.f;
// InActive foreground pin alpha
static const float InactivePinForegroundAlpha = 0.15f;
// Active background pin alpha
static const float ActivePinBackgroundAlpha = 0.8f;
// InActive background pin alpha
static const float InactivePinBackgroundAlpha = 0.4f;
};
void RigElementDetails_GetCustomizedInfo(TSharedRef<IPropertyHandle> InStructPropertyHandle, UControlRigBlueprint*& OutBlueprint)
{
TArray<UObject*> Objects;
InStructPropertyHandle->GetOuterObjects(Objects);
for (UObject* Object : Objects)
{
if (Object->IsA<UControlRigBlueprint>())
{
OutBlueprint = Cast<UControlRigBlueprint>(Object);
if (OutBlueprint)
{
break;
}
}
}
if (OutBlueprint == nullptr)
{
TArray<UPackage*> Packages;
InStructPropertyHandle->GetOuterPackages(Packages);
for (UPackage* Package : Packages)
{
if (Package == nullptr)
{
continue;
}
TArray<UObject*> SubObjects;
Package->GetDefaultSubobjects(SubObjects);
for (UObject* SubObject : SubObjects)
{
if (UControlRig* Rig = Cast<UControlRig>(SubObject))
{
OutBlueprint = Cast<UControlRigBlueprint>(Rig->GetClass()->ClassGeneratedBy);
if (OutBlueprint)
{
break;
}
}
}
if (OutBlueprint)
{
break;
}
}
}
}
UControlRigBlueprint* RigElementDetails_GetBlueprintFromHierarchy(URigHierarchy* InHierarchy)
{
if(InHierarchy == nullptr)
{
return nullptr;
}
UControlRigBlueprint* Blueprint = InHierarchy->GetTypedOuter<UControlRigBlueprint>();
if(Blueprint == nullptr)
{
UControlRig* Rig = InHierarchy->GetTypedOuter<UControlRig>();
if(Rig)
{
Blueprint = Cast<UControlRigBlueprint>(Rig->GetClass()->ClassGeneratedBy);
}
}
return Blueprint;
}
void FRigElementKeyDetails::CustomizeHeader(TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
BlueprintBeingCustomized = nullptr;
RigElementDetails_GetCustomizedInfo(InStructPropertyHandle, BlueprintBeingCustomized);
UControlRigGraph* RigGraph = nullptr;
if(BlueprintBeingCustomized)
{
for (UEdGraph* Graph : BlueprintBeingCustomized->UbergraphPages)
{
RigGraph = Cast<UControlRigGraph>(Graph);
if (RigGraph)
{
break;
}
}
}
// only allow blueprints with at least one rig graph
if (RigGraph == nullptr)
{
BlueprintBeingCustomized = nullptr;
}
if (BlueprintBeingCustomized == nullptr)
{
HeaderRow
.NameContent()
[
InStructPropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
InStructPropertyHandle->CreatePropertyValueWidget()
];
}
else
{
TypeHandle = InStructPropertyHandle->GetChildHandle(TEXT("Type"));
NameHandle = InStructPropertyHandle->GetChildHandle(TEXT("Name"));
TypeHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda(
[this]()
{
this->UpdateElementNameList();
SetElementName(FString());
}
));
UpdateElementNameList();
HeaderRow
.NameContent()
[
InStructPropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
.MinDesiredWidth(250.f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
[
TypeHandle->CreatePropertyValueWidget()
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.f, 0.f, 0.f, 0.f)
[
SAssignNew(SearchableComboBox, SSearchableComboBox)
.OptionsSource(&ElementNameList)
.OnSelectionChanged(this, &FRigElementKeyDetails::OnElementNameChanged)
.OnGenerateWidget(this, &FRigElementKeyDetails::OnGetElementNameWidget)
.Content()
[
SNew(STextBlock)
.Text(this, &FRigElementKeyDetails::GetElementNameAsText)
.Font(IDetailLayoutBuilder::GetDetailFont())
]
]
// Use button
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(1,0)
.VAlign(VAlign_Center)
[
SAssignNew(UseSelectedButton, SButton)
.ButtonStyle( FEditorStyle::Get(), "NoBorder" )
.ButtonColorAndOpacity_Lambda([this]() { return OnGetWidgetBackground(UseSelectedButton); })
.OnClicked(this, &FRigElementKeyDetails::OnGetSelectedClicked)
.ContentPadding(1.f)
.ToolTipText(NSLOCTEXT("GraphEditor", "ObjectGraphPin_Use_Tooltip", "Use item selected"))
[
SNew(SImage)
.ColorAndOpacity_Lambda( [this]() { return OnGetWidgetForeground(UseSelectedButton); })
.Image(FEditorStyle::GetBrush("Icons.CircleArrowLeft"))
]
]
// Select in hierarchy button
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(1,0)
.VAlign(VAlign_Center)
[
SAssignNew(SelectElementButton, SButton)
.ButtonStyle( FEditorStyle::Get(), "NoBorder" )
.ButtonColorAndOpacity_Lambda([this]() { return OnGetWidgetBackground(SelectElementButton); })
.OnClicked(this, &FRigElementKeyDetails::OnSelectInHierarchyClicked)
.ContentPadding(0)
.ToolTipText(NSLOCTEXT("GraphEditor", "ObjectGraphPin_Browse_Tooltip", "Select in hierarchy"))
[
SNew(SImage)
.ColorAndOpacity_Lambda( [this]() { return OnGetWidgetForeground(SelectElementButton); })
.Image(FEditorStyle::GetBrush("Icons.Search"))
]
]
];
}
}
void FRigElementKeyDetails::CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
if (InStructPropertyHandle->IsValidHandle())
{
// only fill the children if the blueprint cannot be found
if (BlueprintBeingCustomized == nullptr)
{
uint32 NumChildren = 0;
InStructPropertyHandle->GetNumChildren(NumChildren);
for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ChildIndex++)
{
StructBuilder.AddProperty(InStructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef());
}
}
}
}
ERigElementType FRigElementKeyDetails::GetElementType() const
{
ERigElementType ElementType = ERigElementType::None;
if (TypeHandle.IsValid())
{
uint8 Index = 0;
TypeHandle->GetValue(Index);
ElementType = (ERigElementType)Index;
}
return ElementType;
}
FString FRigElementKeyDetails::GetElementName() const
{
FString ElementNameStr;
if (NameHandle.IsValid())
{
FName ElementName;
NameHandle->GetValue(ElementName);
ElementNameStr = ElementName.ToString();
}
return ElementNameStr;
}
void FRigElementKeyDetails::SetElementName(FString InName)
{
if (NameHandle.IsValid())
{
NameHandle->SetValue(InName);
}
}
void FRigElementKeyDetails::UpdateElementNameList()
{
if (!TypeHandle.IsValid())
{
return;
}
ElementNameList.Reset();
if (BlueprintBeingCustomized)
{
for (UEdGraph* Graph : BlueprintBeingCustomized->UbergraphPages)
{
if (UControlRigGraph* RigGraph = Cast<UControlRigGraph>(Graph))
{
ElementNameList = *RigGraph->GetElementNameList(GetElementType());
if(SearchableComboBox.IsValid())
{
SearchableComboBox->RefreshOptions();
}
return;
}
}
}
}
void FRigElementKeyDetails::OnElementNameChanged(TSharedPtr<FString> InItem, ESelectInfo::Type InSelectionInfo)
{
if (InItem.IsValid())
{
SetElementName(*InItem);
}
else
{
SetElementName(FString());
}
}
TSharedRef<SWidget> FRigElementKeyDetails::OnGetElementNameWidget(TSharedPtr<FString> InItem)
{
return SNew(STextBlock)
.Text(FText::FromString(InItem.IsValid() ? *InItem : FString()))
.Font(IDetailLayoutBuilder::GetDetailFont());
}
FText FRigElementKeyDetails::GetElementNameAsText() const
{
return FText::FromString(GetElementName());
}
FSlateColor FRigElementKeyDetails::OnGetWidgetForeground(const TSharedPtr<SButton> Button) const
{
float Alpha = (Button.IsValid() && Button->IsHovered()) ? FRigElementKeyDetailsDefs::ActivePinForegroundAlpha : FRigElementKeyDetailsDefs::InactivePinForegroundAlpha;
return FSlateColor(FLinearColor(1.f, 1.f, 1.f, Alpha));
}
FSlateColor FRigElementKeyDetails::OnGetWidgetBackground(const TSharedPtr<SButton> Button) const
{
float Alpha = (Button.IsValid() && Button->IsHovered()) ? FRigElementKeyDetailsDefs::ActivePinBackgroundAlpha : FRigElementKeyDetailsDefs::InactivePinBackgroundAlpha;
return FSlateColor(FLinearColor(1.f, 1.f, 1.f, Alpha));
}
FReply FRigElementKeyDetails::OnGetSelectedClicked()
{
if (BlueprintBeingCustomized)
{
const TArray<FRigElementKey>& Selected = BlueprintBeingCustomized->Hierarchy->GetSelectedKeys();
if (Selected.Num() > 0)
{
if (TypeHandle.IsValid())
{
uint8 Index = (uint8) Selected[0].Type;
TypeHandle->SetValue(Index);
}
SetElementName(Selected[0].Name.ToString());
}
}
return FReply::Handled();
}
FReply FRigElementKeyDetails::OnSelectInHierarchyClicked()
{
if (BlueprintBeingCustomized)
{
FRigElementKey Key;
if (TypeHandle.IsValid())
{
uint8 Type;
TypeHandle->GetValue(Type);
Key.Type = (ERigElementType) Type;
}
if (NameHandle.IsValid())
{
NameHandle->GetValue(Key.Name);
}
if (Key.IsValid())
{
BlueprintBeingCustomized->GetHierarchyController()->SetSelection({Key});
}
}
return FReply::Handled();
}
void FRigUnitDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
TArray<TSharedPtr<FStructOnScope>> StructsBeingCustomized;
DetailBuilder.GetStructsBeingCustomized(StructsBeingCustomized);
if (StructsBeingCustomized.Num() == 0)
{
return;
}
TSharedPtr<FStructOnScope> StructBeingCustomized = StructsBeingCustomized[0];
BlueprintBeingCustomized = nullptr;
if (UPackage* Package = StructBeingCustomized->GetPackage())
{
TArray<UObject*> SubObjects;
Package->GetDefaultSubobjects(SubObjects);
for (UObject* SubObject : SubObjects)
{
if (UControlRig* Rig = Cast<UControlRig>(SubObject))
{
BlueprintBeingCustomized = Cast<UControlRigBlueprint>(Rig->GetClass()->ClassGeneratedBy);
if (BlueprintBeingCustomized)
{
break;
}
}
}
}
if (BlueprintBeingCustomized == nullptr)
{
return;
}
GraphBeingCustomized = nullptr;
for (UEdGraph* Graph : BlueprintBeingCustomized->UbergraphPages)
{
GraphBeingCustomized = Cast<UControlRigGraph>(Graph);
if (GraphBeingCustomized)
{
break;
}
}
if (GraphBeingCustomized == nullptr)
{
return;
}
URigVMGraph* Model = GraphBeingCustomized->GetModel();
if(Model == nullptr)
{
return;
}
const TArray<FName> SelectedNodeNames = Model->GetSelectNodes();
if(SelectedNodeNames.Num() == 0)
{
return;
}
URigVMNode* ModelNode = Model->FindNodeByName(SelectedNodeNames[0]);
if(ModelNode == nullptr)
{
return;
}
UScriptStruct* ScriptStruct = Cast<UScriptStruct>((UStruct*)StructBeingCustomized->GetStruct());
check(ScriptStruct);
IDetailCategoryBuilder& CategoryBuilder = DetailBuilder.EditCategory(*ScriptStruct->GetDisplayNameText().ToString());
for (TFieldIterator<FProperty> PropertyIt(ScriptStruct); PropertyIt; ++PropertyIt)
{
FProperty* Property = *PropertyIt;
TSharedPtr<IPropertyHandle> PropertyHandle = DetailBuilder.GetProperty(Property->GetFName(), ScriptStruct);
if (!PropertyHandle->IsValidHandle())
{
continue;
}
DetailBuilder.HideProperty(PropertyHandle);
URigVMPin* ModelPin = ModelNode->FindPin(Property->GetName());
if(ModelPin == nullptr)
{
continue;
}
if(ModelPin->IsBoundToVariable())
{
CategoryBuilder.AddCustomRow(FText::FromString(Property->GetName()))
.NameContent()
[
PropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew(SControlRigVariableBinding)
.ModelPin(ModelPin)
.Blueprint(BlueprintBeingCustomized)
];
continue;
}
if (FNameProperty* NameProperty = CastField<FNameProperty>(Property))
{
FString CustomWidgetName = NameProperty->GetMetaData(TEXT("CustomWidget"));
if (!CustomWidgetName.IsEmpty())
{
const TArray<TSharedPtr<FString>>* NameList = nullptr;
if (CustomWidgetName == TEXT("BoneName"))
{
NameList = GraphBeingCustomized->GetBoneNameList();
}
else if (CustomWidgetName == TEXT("ControlName"))
{
NameList = GraphBeingCustomized->GetControlNameList();
}
else if (CustomWidgetName == TEXT("SpaceName"))
{
NameList = GraphBeingCustomized->GetNullNameList();
}
else if (CustomWidgetName == TEXT("CurveName"))
{
NameList = GraphBeingCustomized->GetCurveNameList();
}
if (NameList)
{
TSharedPtr<SControlRigGraphPinNameListValueWidget> NameListWidget;
CategoryBuilder.AddCustomRow(FText::FromString(Property->GetName()))
.NameContent()
[
PropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
SAssignNew(NameListWidget, SControlRigGraphPinNameListValueWidget)
.OptionsSource(NameList)
.OnGenerateWidget(this, &FRigUnitDetails::MakeNameListItemWidget)
.OnSelectionChanged(this, &FRigUnitDetails::OnNameListChanged, StructBeingCustomized, NameProperty, DetailBuilder.GetPropertyUtilities())
.OnComboBoxOpening(this, &FRigUnitDetails::OnNameListComboBox, StructBeingCustomized, NameProperty, NameList)
.InitiallySelectedItem(GetCurrentlySelectedItem(StructBeingCustomized, NameProperty, NameList))
.Content()
[
SNew(STextBlock)
.Text(this, &FRigUnitDetails::GetNameListText, StructBeingCustomized, NameProperty)
]
];
NameListWidgets.Add(Property->GetFName(), NameListWidget);
}
else
{
CategoryBuilder.AddCustomRow(FText::FromString(Property->GetName()))
.NameContent()
[
PropertyHandle->CreatePropertyNameWidget()
];
}
continue;
}
}
else if (FStructProperty* StructProperty = CastField<FStructProperty>(Property))
{
const FSimpleDelegate OnStructContentsChangedDelegate = FSimpleDelegate::CreateSP(this, &FRigUnitDetails::OnStructContentsChanged, Property, DetailBuilder.GetPropertyUtilities());
PropertyHandle->SetOnPropertyValueChanged(OnStructContentsChangedDelegate);
PropertyHandle->SetOnChildPropertyValueChanged(OnStructContentsChangedDelegate);
}
CategoryBuilder.AddProperty(PropertyHandle);
}
}
TSharedRef<SWidget> FRigUnitDetails::MakeNameListItemWidget(TSharedPtr<FString> InItem)
{
return SNew(STextBlock).Text(FText::FromString(*InItem));// .Font(FEditorStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont")));
}
FText FRigUnitDetails::GetNameListText(TSharedPtr<FStructOnScope> InStructOnScope, FNameProperty* InProperty) const
{
if (FName* Value = InProperty->ContainerPtrToValuePtr<FName>(InStructOnScope->GetStructMemory()))
{
return FText::FromName(*Value);
}
return FText();
}
TSharedPtr<FString> FRigUnitDetails::GetCurrentlySelectedItem(TSharedPtr<FStructOnScope> InStructOnScope, FNameProperty* InProperty, const TArray<TSharedPtr<FString>>* InNameList) const
{
FString CurrentItem = GetNameListText(InStructOnScope, InProperty).ToString();
for (const TSharedPtr<FString>& Item : *InNameList)
{
if (Item->Equals(CurrentItem))
{
return Item;
}
}
return TSharedPtr<FString>();
}
void FRigUnitDetails::SetNameListText(const FText& NewTypeInValue, ETextCommit::Type /*CommitInfo*/, TSharedPtr<FStructOnScope> InStructOnScope, FNameProperty* InProperty, TSharedRef<IPropertyUtilities> PropertyUtilities)
{
if (FName* Value = InProperty->ContainerPtrToValuePtr<FName>(InStructOnScope->GetStructMemory()))
{
*Value = *NewTypeInValue.ToString();
FPropertyChangedEvent ChangeEvent(InProperty, EPropertyChangeType::ValueSet);
PropertyUtilities->NotifyFinishedChangingProperties(ChangeEvent);
}
}
void FRigUnitDetails::OnNameListChanged(TSharedPtr<FString> NewSelection, ESelectInfo::Type SelectInfo, TSharedPtr<FStructOnScope> InStructOnScope, FNameProperty* InProperty, TSharedRef<IPropertyUtilities> PropertyUtilities)
{
if (SelectInfo != ESelectInfo::Direct)
{
FString NewValue = *NewSelection.Get();
SetNameListText(FText::FromString(NewValue), ETextCommit::OnEnter, InStructOnScope, InProperty, PropertyUtilities);
}
}
void FRigUnitDetails::OnNameListComboBox(TSharedPtr<FStructOnScope> InStructOnScope, FNameProperty* InProperty, const TArray<TSharedPtr<FString>>* InNameList)
{
TSharedPtr<SControlRigGraphPinNameListValueWidget> Widget = NameListWidgets.FindChecked(InProperty->GetFName());
const TSharedPtr<FString> CurrentlySelected = GetCurrentlySelectedItem(InStructOnScope, InProperty, InNameList);
Widget->SetSelectedItem(CurrentlySelected);
}
void FRigUnitDetails::OnStructContentsChanged(FProperty* InProperty, const TSharedRef<IPropertyUtilities> PropertyUtilities)
{
const FPropertyChangedEvent ChangeEvent(InProperty, EPropertyChangeType::ValueSet);
PropertyUtilities->NotifyFinishedChangingProperties(ChangeEvent);
}
void FRigComputedTransformDetails::CustomizeHeader(TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
BlueprintBeingCustomized = nullptr;
RigElementDetails_GetCustomizedInfo(InStructPropertyHandle, BlueprintBeingCustomized);
}
void FRigComputedTransformDetails::CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
TransformHandle = InStructPropertyHandle->GetChildHandle(TEXT("Transform"));
StructBuilder
.AddProperty(TransformHandle.ToSharedRef())
.DisplayName(InStructPropertyHandle->GetPropertyDisplayName());
FString PropertyPath = TransformHandle->GeneratePathToProperty();
if(PropertyPath.StartsWith(TEXT("Struct.")))
{
PropertyPath.RightChopInline(7);
}
if(PropertyPath.StartsWith(TEXT("Pose.")))
{
PropertyPath.RightChopInline(5);
PropertyChain.AddTail(FRigTransformElement::StaticStruct()->FindPropertyByName(TEXT("Pose")));
}
else if(PropertyPath.StartsWith(TEXT("Offset.")))
{
PropertyPath.RightChopInline(7);
PropertyChain.AddTail(FRigControlElement::StaticStruct()->FindPropertyByName(TEXT("Offset")));
}
else if(PropertyPath.StartsWith(TEXT("Gizmo.")))
{
PropertyPath.RightChopInline(6);
PropertyChain.AddTail(FRigControlElement::StaticStruct()->FindPropertyByName(TEXT("Gizmo")));
}
if(PropertyPath.StartsWith(TEXT("Current.")))
{
PropertyPath.RightChopInline(8);
PropertyChain.AddTail(FRigCurrentAndInitialTransform::StaticStruct()->FindPropertyByName(TEXT("Current")));
}
else if(PropertyPath.StartsWith(TEXT("Initial.")))
{
PropertyPath.RightChopInline(8);
PropertyChain.AddTail(FRigCurrentAndInitialTransform::StaticStruct()->FindPropertyByName(TEXT("Initial")));
}
if(PropertyPath.StartsWith(TEXT("Local.")))
{
PropertyPath.RightChopInline(6);
PropertyChain.AddTail(FRigLocalAndGlobalTransform::StaticStruct()->FindPropertyByName(TEXT("Local")));
}
else if(PropertyPath.StartsWith(TEXT("Global.")))
{
PropertyPath.RightChopInline(7);
PropertyChain.AddTail(FRigLocalAndGlobalTransform::StaticStruct()->FindPropertyByName(TEXT("Global")));
}
PropertyChain.AddTail(TransformHandle->GetProperty());
PropertyChain.SetActiveMemberPropertyNode(PropertyChain.GetTail()->GetValue());
const FSimpleDelegate OnTransformChangedDelegate = FSimpleDelegate::CreateSP(this, &FRigComputedTransformDetails::OnTransformChanged, &PropertyChain);
TransformHandle->SetOnPropertyValueChanged(OnTransformChangedDelegate);
TransformHandle->SetOnChildPropertyValueChanged(OnTransformChangedDelegate);
}
void FRigComputedTransformDetails::OnTransformChanged(FEditPropertyChain* InPropertyChain)
{
if(BlueprintBeingCustomized && InPropertyChain)
{
if(InPropertyChain->Num() > 1)
{
FPropertyChangedEvent ChangeEvent(InPropertyChain->GetHead()->GetValue(), EPropertyChangeType::ValueSet);
ChangeEvent.SetActiveMemberProperty(InPropertyChain->GetTail()->GetValue());
FPropertyChangedChainEvent ChainEvent(*InPropertyChain, ChangeEvent);
BlueprintBeingCustomized->BroadcastPostEditChangeChainProperty(ChainEvent);
}
}
}
void FRigBaseElementDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
DetailBuilder.HideProperty(TEXT("Key"), FRigBaseElement::StaticStruct());
DetailBuilder.HideProperty(TEXT("Index"), FRigBaseElement::StaticStruct());
DetailBuilder.HideProperty(TEXT("SubIndex"), FRigBaseElement::StaticStruct());
TArray<TSharedPtr<FStructOnScope>> StructsBeingCustomized;
DetailBuilder.GetStructsBeingCustomized(StructsBeingCustomized);
for (TSharedPtr<FStructOnScope> StructBeingCustomized : StructsBeingCustomized)
{
if (UPackage* Package = StructBeingCustomized->GetPackage())
{
TArray<UObject*> SubObjects;
Package->GetDefaultSubobjects(SubObjects);
for (UObject* SubObject : SubObjects)
{
if (UControlRig* Rig = Cast<UControlRig>(SubObject))
{
BlueprintBeingCustomized = Cast<UControlRigBlueprint>(Rig->GetClass()->ClassGeneratedBy);
if(BlueprintBeingCustomized)
{
HierarchyBeingCustomized = BlueprintBeingCustomized->Hierarchy;
if (UControlRig* DebuggedControlRig = Cast<UControlRig>(BlueprintBeingCustomized->GetObjectBeingDebugged()))
{
if (!DebuggedControlRig->IsSetupModeEnabled())
{
HierarchyBeingCustomized = DebuggedControlRig->GetHierarchy();
}
}
break;
}
}
}
if (HierarchyBeingCustomized)
{
ElementKeyBeingCustomized = ((const FRigBaseElement*)StructBeingCustomized->GetStructMemory())->GetKey();
break;
}
}
}
IDetailCategoryBuilder& Category = DetailBuilder.EditCategory(TEXT("RigElement"));
Category.InitiallyCollapsed(false);
Category.AddCustomRow(FText::FromString(TEXT("Name")))
.NameContent()
[
SNew(STextBlock)
.Text(FText::FromString(TEXT("Name")))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
.ValueContent()
[
SNew(SEditableTextBox)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(this, &FRigBaseElementDetails::GetName)
.OnTextCommitted(this, &FRigBaseElementDetails::SetName)
];
}
void FRigBaseElementDetails::SetName(const FText& InNewText, ETextCommit::Type InCommitType)
{
URigHierarchy* Hierarchy = nullptr;
if (BlueprintBeingCustomized)
{
Hierarchy = BlueprintBeingCustomized->Hierarchy;
}
else
{
Hierarchy = GetHierarchy();
}
if (Hierarchy)
{
URigHierarchyController* Controller = Hierarchy->GetController(true);
check(Controller);
Controller->RenameElement(ElementKeyBeingCustomized, *InNewText.ToString(), true);
}
}
void FRigBaseElementDetails::OnStructContentsChanged(FProperty* InProperty, const TSharedRef<IPropertyUtilities> PropertyUtilities)
{
const FPropertyChangedEvent ChangeEvent(InProperty, EPropertyChangeType::ValueSet);
PropertyUtilities->NotifyFinishedChangingProperties(ChangeEvent);
}
bool FRigBaseElementDetails::IsSetupModeEnabled() const
{
if(BlueprintBeingCustomized)
{
if (UControlRig* DebuggedRig = Cast<UControlRig>(BlueprintBeingCustomized->GetObjectBeingDebugged()))
{
return DebuggedRig->IsSetupModeEnabled();
}
}
return false;
}
void FRigTransformElementDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
FRigBaseElementDetails::CustomizeDetails(DetailBuilder);
//if(ElementKeyBeingCustomized.Type != ERigElementType::Control)
{
IDetailCategoryBuilder& Category = DetailBuilder.EditCategory(TEXT("Pose"));
if(ElementKeyBeingCustomized.Type == ERigElementType::Control)
{
Category
.InitiallyCollapsed(true);
}
// setup initial global
{
const TSharedRef<IPropertyHandle> PropertyHandle = DetailBuilder.GetProperty(TEXT("Pose.Initial.Global.Transform"), FRigTransformElement::StaticStruct());
Category.AddProperty(PropertyHandle, EPropertyLocation::Advanced).DisplayName(FText::FromString(TEXT("Initial Global")))
.IsEnabled(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateSP(this, &FRigBaseElementDetails::IsSetupModeEnabled)));
PoseInitialGlobal.AddHead(FRigControlElement::StaticStruct()->FindPropertyByName(TEXT("Pose")));
PoseInitialGlobal.AddTail(FRigCurrentAndInitialTransform::StaticStruct()->FindPropertyByName(TEXT("Initial")));
PoseInitialGlobal.AddTail(FRigLocalAndGlobalTransform::StaticStruct()->FindPropertyByName(TEXT("Global")));
PoseInitialGlobal.AddTail(FRigComputedTransform::StaticStruct()->FindPropertyByName(TEXT("Transform")));
PoseInitialGlobal.SetActiveMemberPropertyNode(PoseInitialGlobal.GetTail()->GetValue());
const FSimpleDelegate OnTransformChangedDelegate = FSimpleDelegate::CreateStatic(&FRigTransformElementDetails::OnTransformChanged, &PoseInitialGlobal, BlueprintBeingCustomized);
PropertyHandle->SetOnPropertyValueChanged(OnTransformChangedDelegate);
PropertyHandle->SetOnChildPropertyValueChanged(OnTransformChangedDelegate);
PropertyHandle->SetToolTipText(FText::FromString(TEXT("The initial / ref pose global transform in the space of the rig / actor.")));
}
// setup initial local
{
const TSharedRef<IPropertyHandle> PropertyHandle = DetailBuilder.GetProperty(TEXT("Pose.Initial.Local.Transform"), FRigTransformElement::StaticStruct());
Category.AddProperty(PropertyHandle, EPropertyLocation::Advanced).DisplayName(FText::FromString(TEXT("Initial Local")))
.IsEnabled(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateSP(this, &FRigBaseElementDetails::IsSetupModeEnabled)));
PoseInitialLocal.AddHead(FRigControlElement::StaticStruct()->FindPropertyByName(TEXT("Pose")));
PoseInitialLocal.AddTail(FRigCurrentAndInitialTransform::StaticStruct()->FindPropertyByName(TEXT("Initial")));
PoseInitialLocal.AddTail(FRigLocalAndGlobalTransform::StaticStruct()->FindPropertyByName(TEXT("Local")));
PoseInitialLocal.AddTail(FRigComputedTransform::StaticStruct()->FindPropertyByName(TEXT("Transform")));
PoseInitialLocal.SetActiveMemberPropertyNode(PoseInitialLocal.GetTail()->GetValue());
const FSimpleDelegate OnTransformChangedDelegate = FSimpleDelegate::CreateStatic(&FRigTransformElementDetails::OnTransformChanged, &PoseInitialLocal, BlueprintBeingCustomized);
PropertyHandle->SetOnPropertyValueChanged(OnTransformChangedDelegate);
PropertyHandle->SetOnChildPropertyValueChanged(OnTransformChangedDelegate);
PropertyHandle->SetToolTipText(FText::FromString(TEXT("The initial / ref pose local transform in the space of the parent.\nFor Controls the initial value is relative to the offset.")));
}
// setup current global
{
const TSharedRef<IPropertyHandle> PropertyHandle = DetailBuilder.GetProperty(TEXT("Pose.Current.Global.Transform"), FRigTransformElement::StaticStruct());
Category.AddProperty(PropertyHandle, EPropertyLocation::Advanced).DisplayName(FText::FromString(TEXT("Current Global")))
.IsEnabled(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateSP(this, &FRigBaseElementDetails::IsSetupModeEnabled)));
PoseCurrentGlobal.AddHead(FRigControlElement::StaticStruct()->FindPropertyByName(TEXT("Pose")));
PoseCurrentGlobal.AddTail(FRigCurrentAndInitialTransform::StaticStruct()->FindPropertyByName(TEXT("Current")));
PoseCurrentGlobal.AddTail(FRigLocalAndGlobalTransform::StaticStruct()->FindPropertyByName(TEXT("Global")));
PoseCurrentGlobal.AddTail(FRigComputedTransform::StaticStruct()->FindPropertyByName(TEXT("Transform")));
PoseCurrentGlobal.SetActiveMemberPropertyNode(PoseCurrentGlobal.GetTail()->GetValue());
const FSimpleDelegate OnTransformChangedDelegate = FSimpleDelegate::CreateStatic(&FRigTransformElementDetails::OnTransformChanged, &PoseCurrentGlobal, BlueprintBeingCustomized);
PropertyHandle->SetOnPropertyValueChanged(OnTransformChangedDelegate);
PropertyHandle->SetOnChildPropertyValueChanged(OnTransformChangedDelegate);
PropertyHandle->SetToolTipText(FText::FromString(TEXT("The current global transform in the space of the rig / actor.")));
}
// setup current local
{
const TSharedRef<IPropertyHandle> PropertyHandle = DetailBuilder.GetProperty(TEXT("Pose.Current.Local.Transform"), FRigTransformElement::StaticStruct());
Category.AddProperty(PropertyHandle).DisplayName(FText::FromString(TEXT("Current Local")))
.IsEnabled(ElementKeyBeingCustomized.Type != ERigElementType::Control);
PoseCurrentLocal.AddHead(FRigControlElement::StaticStruct()->FindPropertyByName(TEXT("Pose")));
PoseCurrentLocal.AddTail(FRigCurrentAndInitialTransform::StaticStruct()->FindPropertyByName(TEXT("Current")));
PoseCurrentLocal.AddTail(FRigLocalAndGlobalTransform::StaticStruct()->FindPropertyByName(TEXT("Local")));
PoseCurrentLocal.AddTail(FRigComputedTransform::StaticStruct()->FindPropertyByName(TEXT("Transform")));
PoseCurrentLocal.SetActiveMemberPropertyNode(PoseCurrentLocal.GetTail()->GetValue());
const FSimpleDelegate OnTransformChangedDelegate = FSimpleDelegate::CreateStatic(&FRigTransformElementDetails::OnTransformChanged, &PoseCurrentLocal, BlueprintBeingCustomized);
PropertyHandle->SetOnPropertyValueChanged(OnTransformChangedDelegate);
PropertyHandle->SetOnChildPropertyValueChanged(OnTransformChangedDelegate);
PropertyHandle->SetToolTipText(FText::FromString(TEXT("The current local transform in the space of the parent.\nFor Controls the initial value is relative to the offset.")));
}
DetailBuilder.HideProperty(TEXT("Pose"), FRigTransformElement::StaticStruct());
}
}
void FRigTransformElementDetails::OnTransformChanged(FEditPropertyChain* InPropertyChain, UControlRigBlueprint* InBlueprint)
{
if(InBlueprint && InPropertyChain)
{
if(InPropertyChain->Num() > 1)
{
FPropertyChangedEvent ChangeEvent(InPropertyChain->GetHead()->GetValue(), EPropertyChangeType::ValueSet);
ChangeEvent.SetActiveMemberProperty(InPropertyChain->GetTail()->GetValue());
FPropertyChangedChainEvent ChainEvent(*InPropertyChain, ChangeEvent);
InBlueprint->BroadcastPostEditChangeChainProperty(ChainEvent);
}
}
}
void FRigBoneElementDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{
FRigTransformElementDetails::CustomizeDetails(DetailBuilder);
}
void FRigControlElementDetails_SetupBoolValueWidget(IDetailCategoryBuilder& InCategory, ERigControlValueType InValueType, FRigControlElement* InControlElement, URigHierarchy* InHierarchy)
{
UEnum* ControlTypeEnum = StaticEnum<ERigControlType>();
UEnum* ValueTypeEnum = StaticEnum<ERigControlValueType>();
const FString ValueTypeName = ValueTypeEnum->GetDisplayNameTextByValue((int64)InValueType).ToString();
const FText PropertyLabel = FText::FromString(FString::Printf(TEXT("%s Value"), *ValueTypeName));
TWeakObjectPtr<URigHierarchy> HierarchyPtr = InHierarchy;
const FRigElementKey Key = InControlElement->GetKey();
InCategory.AddCustomRow(PropertyLabel)
.NameContent()
[
SNew(STextBlock)
.Text(PropertyLabel)
.Font(IDetailLayoutBuilder::GetDetailFont())
]
.ValueContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
SNew(SCheckBox)
.IsChecked_Lambda([HierarchyPtr, Key, InValueType]() -> ECheckBoxState
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
bool Value = HierarchyPtr->GetControlValue(ControlElement, InValueType).Get<bool>();
return Value ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
}
return ECheckBoxState::Unchecked;
})
.OnCheckStateChanged_Lambda([HierarchyPtr, Key, InValueType](ECheckBoxState NewState)
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
const FRigControlValue Value = FRigControlValue::Make<bool>(NewState == ECheckBoxState::Checked);
HierarchyPtr->SetControlValue(ControlElement->GetKey(), Value, InValueType, true);
if(InValueType == ERigControlValueType::Initial)
{
if(UControlRigBlueprint* Blueprint = RigElementDetails_GetBlueprintFromHierarchy(HierarchyPtr.Get()))
{
Blueprint->Hierarchy->SetControlValue(ControlElement->GetKey(), Value, InValueType, true);
}
}
}
}
})
]
]
.IsEnabled(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateLambda([HierarchyPtr, Key, InValueType]()->bool
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
return ControlElement->Settings.IsValueTypeEnabled(InValueType);
}
}
return false;
})));
}
void FRigControlElementDetails_SetupIntegerValueWidget(IDetailCategoryBuilder& InCategory, ERigControlValueType InValueType, FRigControlElement* InControlElement, URigHierarchy* InHierarchy)
{
UEnum* ControlTypeEnum = StaticEnum<ERigControlType>();
UEnum* ValueTypeEnum = StaticEnum<ERigControlValueType>();
const FString ValueTypeName = ValueTypeEnum->GetDisplayNameTextByValue((int64)InValueType).ToString();
const FText PropertyLabel = FText::FromString(FString::Printf(TEXT("%s Value"), *ValueTypeName));
TWeakObjectPtr<URigHierarchy> HierarchyPtr = InHierarchy;
const FRigElementKey Key = InControlElement->GetKey();
const TAttribute<bool> EnabledAttribute = TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateLambda([HierarchyPtr, Key, InValueType]()->bool
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
return ControlElement->Settings.IsValueTypeEnabled(InValueType);
}
}
return false;
}));
const TAttribute<EVisibility> VisibilityAttribute = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateLambda([EnabledAttribute]()->EVisibility
{
return EnabledAttribute.Get() ? EVisibility::Visible : EVisibility::Hidden;
}));
if (InControlElement->Settings.ControlEnum)
{
InCategory.AddCustomRow(PropertyLabel)
.Visibility(VisibilityAttribute)
.NameContent()
[
SNew(STextBlock)
.Text(PropertyLabel)
.Font(IDetailLayoutBuilder::GetDetailFont())
]
.ValueContent()
.MinDesiredWidth(125.0f * 3.0f) // copied from FComponentTransformDetails
.MaxDesiredWidth(125.0f * 3.0f)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
SNew(SEnumComboBox, InControlElement->Settings.ControlEnum)
.CurrentValue_Lambda([HierarchyPtr, Key, InValueType]() -> int32
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
return HierarchyPtr->GetControlValue(ControlElement, InValueType).Get<int32>();
}
}
return 0;
})
.OnEnumSelectionChanged_Lambda([HierarchyPtr, Key, InValueType](int32 NewSelection, ESelectInfo::Type)
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
const FRigControlValue Value = FRigControlValue::Make<int32>(NewSelection);
HierarchyPtr->SetControlValue(ControlElement->GetKey(), Value, InValueType, true);
if(InValueType == ERigControlValueType::Initial)
{
if(UControlRigBlueprint* Blueprint = RigElementDetails_GetBlueprintFromHierarchy(HierarchyPtr.Get()))
{
Blueprint->Hierarchy->SetControlValue(ControlElement->GetKey(), Value, InValueType, true);
}
}
}
}
})
.Font(FEditorStyle::GetFontStyle(TEXT("MenuItem.Font")))
]
]
.IsEnabled(EnabledAttribute);
}
else
{
InCategory.AddCustomRow(PropertyLabel)
.Visibility(VisibilityAttribute)
.NameContent()
[
SNew(STextBlock)
.Text(PropertyLabel)
.Font(IDetailLayoutBuilder::GetDetailFont())
]
.ValueContent()
.MinDesiredWidth(125.0f * 3.0f) // copied from FComponentTransformDetails
.MaxDesiredWidth(125.0f * 3.0f)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
SNew(SNumericEntryBox<int32>)
.Font(FEditorStyle::GetFontStyle(TEXT("MenuItem.Font")))
.AllowSpin(InValueType == ERigControlValueType::Current || InValueType == ERigControlValueType::Initial)
.MinSliderValue_Lambda([HierarchyPtr, Key, InValueType]() -> TOptional<int32>
{
if(InValueType == ERigControlValueType::Current || InValueType == ERigControlValueType::Initial)
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
return ControlElement->Settings.MinimumValue.Get<int32>();
}
}
}
return TOptional<int32>();
})
.MaxSliderValue_Lambda([HierarchyPtr, Key, InValueType]() -> TOptional<int32>
{
if(InValueType == ERigControlValueType::Current || InValueType == ERigControlValueType::Initial)
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
return ControlElement->Settings.MaximumValue.Get<int32>();
}
}
}
return TOptional<int32>();
})
.Value_Lambda([HierarchyPtr, Key, InValueType]() -> int32
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
return HierarchyPtr->GetControlValue(ControlElement, InValueType).Get<int32>();
}
}
return 0;
})
.OnValueChanged_Lambda([HierarchyPtr, Key, InValueType](TOptional<int32> InNewSelection)
{
if(InNewSelection.IsSet())
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
const FRigControlValue Value = FRigControlValue::Make<int32>(InNewSelection.GetValue());
HierarchyPtr->SetControlValue(ControlElement->GetKey(), Value, InValueType, true);
if(InValueType == ERigControlValueType::Initial)
{
if(UControlRigBlueprint* Blueprint = RigElementDetails_GetBlueprintFromHierarchy(HierarchyPtr.Get()))
{
Blueprint->Hierarchy->SetControlValue(ControlElement->GetKey(), Value, InValueType, true);
}
}
}
}
}
})
]
]
.IsEnabled(EnabledAttribute);
}
}
void FRigControlElementDetails_SetupFloatValueWidget(IDetailCategoryBuilder& InCategory, ERigControlValueType InValueType, FRigControlElement* InControlElement, URigHierarchy* InHierarchy)
{
UEnum* ControlTypeEnum = StaticEnum<ERigControlType>();
UEnum* ValueTypeEnum = StaticEnum<ERigControlValueType>();
const FString ValueTypeName = ValueTypeEnum->GetDisplayNameTextByValue((int64)InValueType).ToString();
const FText PropertyLabel = FText::FromString(FString::Printf(TEXT("%s Value"), *ValueTypeName));
TWeakObjectPtr<URigHierarchy> HierarchyPtr = InHierarchy;
const FRigElementKey Key = InControlElement->GetKey();
const TAttribute<bool> EnabledAttribute = TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateLambda([HierarchyPtr, Key, InValueType]()->bool
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
return ControlElement->Settings.IsValueTypeEnabled(InValueType);
}
}
return false;
}));
const TAttribute<EVisibility> VisibilityAttribute = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateLambda([EnabledAttribute]()->EVisibility
{
return EnabledAttribute.Get() ? EVisibility::Visible : EVisibility::Hidden;
}));
InCategory.AddCustomRow(PropertyLabel)
.Visibility(VisibilityAttribute)
.NameContent()
[
SNew(STextBlock)
.Text(PropertyLabel)
.Font(IDetailLayoutBuilder::GetDetailFont())
]
.ValueContent()
.MinDesiredWidth(125.0f * 3.0f) // copied from FComponentTransformDetails
.MaxDesiredWidth(125.0f * 3.0f)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
SNew(SNumericEntryBox<float>)
.Font(FEditorStyle::GetFontStyle(TEXT("MenuItem.Font")))
.AllowSpin(InValueType == ERigControlValueType::Current || InValueType == ERigControlValueType::Initial)
.Value_Lambda([HierarchyPtr, Key, InValueType]() -> float
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
return HierarchyPtr->GetControlValue(ControlElement, InValueType).Get<float>();
}
}
return 0.f;
})
.MinSliderValue_Lambda([HierarchyPtr, Key, InValueType]() -> TOptional<float>
{
if(InValueType == ERigControlValueType::Current || InValueType == ERigControlValueType::Initial)
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
return ControlElement->Settings.MinimumValue.Get<float>();
}
}
}
return TOptional<float>();
})
.MaxSliderValue_Lambda([HierarchyPtr, Key, InValueType]() -> TOptional<float>
{
if(InValueType == ERigControlValueType::Current || InValueType == ERigControlValueType::Initial)
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
return ControlElement->Settings.MaximumValue.Get<float>();
}
}
}
return TOptional<float>();
})
.OnValueChanged_Lambda([HierarchyPtr, Key, InValueType](TOptional<float> InNewSelection)
{
if(InNewSelection.IsSet())
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
const FRigControlValue Value = FRigControlValue::Make<float>(InNewSelection.GetValue());
HierarchyPtr->SetControlValue(ControlElement->GetKey(), Value, InValueType, true);
if(InValueType == ERigControlValueType::Initial)
{
if(UControlRigBlueprint* Blueprint = RigElementDetails_GetBlueprintFromHierarchy(HierarchyPtr.Get()))
{
Blueprint->Hierarchy->SetControlValue(ControlElement->GetKey(), Value, InValueType, true);
}
}
}
}
}
})
]
]
.IsEnabled(EnabledAttribute);
}
template<typename T>
void FRigControlElementDetails_SetupStructValueWidget(IDetailCategoryBuilder& InCategory, ERigControlValueType InValueType, FRigControlElement* InControlElement, URigHierarchy* InHierarchy)
{
UEnum* ControlTypeEnum = StaticEnum<ERigControlType>();
UEnum* ValueTypeEnum = StaticEnum<ERigControlValueType>();
const FString ValueTypeName = ValueTypeEnum->GetDisplayNameTextByValue((int64)InValueType).ToString();
const FText PropertyLabel = FText::FromString(FString::Printf(TEXT("%s Value"), *ValueTypeName));
const UStruct* ValueStruct = TBaseStructure<T>::Get();
const TSharedPtr<FStructOnScope> StructToDisplay = MakeShareable(new FStructOnScope(ValueStruct));
TWeakObjectPtr<URigHierarchy> HierarchyPtr = InHierarchy;
const FRigElementKey Key = InControlElement->GetKey();
const TAttribute<bool> EnabledAttribute = TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateLambda([HierarchyPtr, Key, InValueType, StructToDisplay, ValueStruct]()->bool
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
// update the struct with the current control value
uint8* StructMemory = StructToDisplay->GetStructMemory();
const FRigControlValue& CurrentValue = HierarchyPtr->GetControlValue(Key, InValueType);
FMemory::Memcpy(StructToDisplay->GetStructMemory(), &CurrentValue.GetRef<T>(), sizeof(T));
return ControlElement->Settings.IsValueTypeEnabled(InValueType);
}
}
return false;
}));
const TAttribute<EVisibility> VisibilityAttribute = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateLambda([HierarchyPtr, Key, InValueType]()->EVisibility
{
if(HierarchyPtr.IsValid())
{
if(FRigControlElement* ControlElement = HierarchyPtr->Find<FRigControlElement>(Key))
{
if(ControlElement->Settings.IsValueTypeEnabled(InValueType))
{
return EVisibility::Visible;
}
}
}
return EVisibility::Hidden;
}));
IDetailPropertyRow* Row = InCategory.AddExternalStructure(StructToDisplay);
Row->DisplayName(PropertyLabel);
Row->ShouldAutoExpand(true);
Row->IsEnabled(EnabledAttribute);
Row->Visibility(VisibilityAttribute);
TSharedPtr<SWidget> NameWidget, ValueWidget;
Row->GetDefaultWidgets(NameWidget, ValueWidget);
const FSimpleDelegate OnStructContentsChangedDelegate = FSimpleDelegate::CreateLambda([HierarchyPtr, Key, StructToDisplay, InValueType]()
{
if(HierarchyPtr.IsValid())
{
const FRigControlValue Value = FRigControlValue::Make(*(T*)StructToDisplay->GetStructMemory());
HierarchyPtr->SetControlValue(Key, Value, InValueType, true);
if(InValueType == ERigControlValueType::Initial)
{
if(UControlRigBlueprint* Blueprint = RigElementDetails_GetBlueprintFromHierarchy(HierarchyPtr.Get()))
{
Blueprint->Hierarchy->SetControlValue(Key, Value, InValueType, true);
}
}
}
});
TSharedPtr<IPropertyHandle> Handle = Row->GetPropertyHandle();
Handle->SetOnPropertyValueChanged(OnStructContentsChangedDelegate);
Handle->SetOnChildPropertyValueChanged(OnStructContentsChangedDelegate);
}
void FRigControlElementDetails_SetupValueWidget(IDetailCategoryBuilder& InCategory, ERigControlValueType InValueType, FRigControlElement* InControlElement, URigHierarchy* InHierarchy)
{
switch(InControlElement->Settings.ControlType)
{
case ERigControlType::Bool:
{
if((InValueType == ERigControlValueType::Minimum) || (InValueType == ERigControlValueType::Maximum))
{
return;
}
FRigControlElementDetails_SetupBoolValueWidget(InCategory, InValueType, InControlElement, InHierarchy);
break;
}
case ERigControlType::Integer:
{
FRigControlElementDetails_SetupIntegerValueWidget(InCategory, InValueType, InControlElement, InHierarchy);
break;
}
case ERigControlType::Float:
{
FRigControlElementDetails_SetupFloatValueWidget(InCategory, InValueType, InControlElement, InHierarchy);
break;
}
case ERigControlType::Vector2D:
{
FRigControlElementDetails_SetupStructValueWidget<FVector2D>(InCategory, InValueType, InControlElement, InHierarchy);
break;
}
case ERigControlType::Position:
case ERigControlType::Scale:
{
FRigControlElementDetails_SetupStructValueWidget<FVector>(InCategory, InValueType, InControlElement, InHierarchy);
break;
}
case ERigControlType::Rotator:
{
FRigControlElementDetails_SetupStructValueWidget<FRotator>(InCategory, InValueType, InControlElement, InHierarchy);
break;
}
case ERigControlType::TransformNoScale:
{
FRigControlElementDetails_SetupStructValueWidget<FTransformNoScale>(InCategory, InValueType, InControlElement, InHierarchy);
break;
}
case ERigControlType::EulerTransform:
{
FRigControlElementDetails_SetupStructValueWidget<FEulerTransform>(InCategory, InValueType, InControlElement, InHierarchy);
break;
}
case ERigControlType::Transform:
{
FRigControlElementDetails_SetupStructValueWidget<FTransform>(InCategory, InValueType, InControlElement, InHierarchy);
break;
}
default:
{
ensure(false);
break;
}
}
}
TArray<TSharedPtr<FString>> FRigControlElementDetails::ControlTypeList;
void FRigControlElementDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
FRigTransformElementDetails::CustomizeDetails(DetailBuilder);
GizmoNameList.Reset();
if (BlueprintBeingCustomized)
{
if (!BlueprintBeingCustomized->GizmoLibrary.IsValid())
{
BlueprintBeingCustomized->GizmoLibrary.LoadSynchronous();
}
if (BlueprintBeingCustomized->GizmoLibrary.IsValid())
{
GizmoNameList.Add(MakeShared<FString>(BlueprintBeingCustomized->GizmoLibrary->DefaultGizmo.GizmoName.ToString()));
for (const FControlRigGizmoDefinition& Gizmo : BlueprintBeingCustomized->GizmoLibrary->Gizmos)
{
GizmoNameList.Add(MakeShared<FString>(Gizmo.GizmoName.ToString()));
}
}
}
if (HierarchyBeingCustomized == nullptr || !ElementKeyBeingCustomized)
{
return;
}
IDetailCategoryBuilder& ControlCategory = DetailBuilder.EditCategory(TEXT("Control"), LOCTEXT("ControlCategory", "Control"));
IDetailCategoryBuilder& LimitsCategory = DetailBuilder.EditCategory(TEXT("Limits"), LOCTEXT("LimitsCategory", "Limits"));
IDetailCategoryBuilder& GizmoCategory = DetailBuilder.EditCategory(TEXT("Gizmo"), LOCTEXT("GizmoCategory", "Gizmo"));
ControlCategory.InitiallyCollapsed(false);
LimitsCategory.InitiallyCollapsed(false);
GizmoCategory.InitiallyCollapsed(false);
const TSharedRef<IPropertyHandle> DisplayNameHandle = DetailBuilder.GetProperty(TEXT("Settings.DisplayName"));
DetailBuilder.HideProperty(DisplayNameHandle);
ControlCategory.AddProperty(DisplayNameHandle).CustomWidget()
.NameContent()
[
SNew(STextBlock)
.Text(FText::FromString(TEXT("Display Name")))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
.ValueContent()
[
SNew(SEditableTextBox)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(this, &FRigControlElementDetails::GetDisplayName)
.OnTextCommitted(this, &FRigControlElementDetails::SetDisplayName, DetailBuilder.GetPropertyUtilities())
];
if (ControlTypeList.Num() == 0)
{
UEnum* Enum = StaticEnum<ERigControlType>();
for (int64 Index = 0; Index < Enum->GetMaxEnumValue(); Index++)
{
ControlTypeList.Add(MakeShared<FString>(Enum->GetDisplayNameTextByValue(Index).ToString()));
}
}
FRigControlElement* ControlElement = HierarchyBeingCustomized->FindChecked<FRigControlElement>(ElementKeyBeingCustomized);
// when control type changes, we have to refresh detail panel
const TSharedRef<IPropertyHandle> ControlTypeHandle = DetailBuilder.GetProperty(TEXT("Settings.ControlType"));
ControlTypeHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda(
[this, &DetailBuilder]()
{
DetailBuilder.ForceRefreshDetails();
if (this->HierarchyBeingCustomized && this->ElementKeyBeingCustomized.IsValid())
{
FRigControlElement* ControlElement = this->HierarchyBeingCustomized->FindChecked<FRigControlElement>(ElementKeyBeingCustomized);
FRigControlValue ValueToSet;
ControlElement->Settings.bLimitTranslation = false;
ControlElement->Settings.bLimitRotation = false;
ControlElement->Settings.bLimitScale = false;
switch (ControlElement->Settings.ControlType)
{
case ERigControlType::Bool:
{
ValueToSet = FRigControlValue::Make<bool>(false);
break;
}
case ERigControlType::Float:
{
ValueToSet = FRigControlValue::Make<float>(0.f);
ControlElement->Settings.bLimitTranslation = true;
ControlElement->Settings.MinimumValue = FRigControlValue::Make<float>(0.f);
ControlElement->Settings.MaximumValue = FRigControlValue::Make<float>(100.f);
break;
}
case ERigControlType::Integer:
{
ValueToSet = FRigControlValue::Make<int32>(0);
ControlElement->Settings.bLimitTranslation = true;
ControlElement->Settings.MinimumValue = FRigControlValue::Make<int32>(0);
ControlElement->Settings.MaximumValue = FRigControlValue::Make<int32>(100);
break;
}
case ERigControlType::Vector2D:
{
ValueToSet = FRigControlValue::Make<FVector2D>(FVector2D::ZeroVector);
ControlElement->Settings.bLimitTranslation = true;
ControlElement->Settings.MinimumValue = FRigControlValue::Make<FVector2D>(FVector2D::ZeroVector);
ControlElement->Settings.MaximumValue = FRigControlValue::Make<FVector2D>(FVector2D(100.f, 100.f));
break;
}
case ERigControlType::Position:
{
ValueToSet = FRigControlValue::Make<FVector>(FVector::ZeroVector);
ControlElement->Settings.MinimumValue = FRigControlValue::Make<FVector>(-FVector::OneVector);
ControlElement->Settings.MaximumValue = FRigControlValue::Make<FVector>(FVector::OneVector);
break;
}
case ERigControlType::Scale:
{
ValueToSet = FRigControlValue::Make<FVector>(FVector::OneVector);
ControlElement->Settings.MinimumValue = FRigControlValue::Make<FVector>(FVector::ZeroVector);
ControlElement->Settings.MaximumValue = FRigControlValue::Make<FVector>(FVector::OneVector);
break;
}
case ERigControlType::Rotator:
{
ValueToSet = FRigControlValue::Make<FRotator>(FRotator::ZeroRotator);
ControlElement->Settings.MinimumValue = FRigControlValue::Make<FRotator>(FRotator::ZeroRotator);
ControlElement->Settings.MaximumValue = FRigControlValue::Make<FRotator>(FRotator(180.f, 180.f, 180.f));
break;
}
case ERigControlType::Transform:
{
ValueToSet = FRigControlValue::Make<FTransform>(FTransform::Identity);
ControlElement->Settings.MinimumValue = ValueToSet;
ControlElement->Settings.MaximumValue = ValueToSet;
break;
}
case ERigControlType::TransformNoScale:
{
FTransformNoScale Identity = FTransform::Identity;
ValueToSet = FRigControlValue::Make<FTransformNoScale>(Identity);
ControlElement->Settings.MinimumValue = ValueToSet;
ControlElement->Settings.MaximumValue = ValueToSet;
break;
}
case ERigControlType::EulerTransform:
{
FEulerTransform Identity = FEulerTransform::Identity;
ValueToSet = FRigControlValue::Make<FEulerTransform>(Identity);
ControlElement->Settings.MinimumValue = ValueToSet;
ControlElement->Settings.MaximumValue = ValueToSet;
break;
}
default:
{
ensure(false);
break;
}
}
this->HierarchyBeingCustomized->Notify(ERigHierarchyNotification::ControlSettingChanged, ControlElement);
this->HierarchyBeingCustomized->SetControlValue(ControlElement, ValueToSet, ERigControlValueType::Initial, true);
this->HierarchyBeingCustomized->SetControlValue(ControlElement, ValueToSet, ERigControlValueType::Current, true);
if (this->HierarchyBeingCustomized != this->BlueprintBeingCustomized->Hierarchy)
{
if(FRigControlElement* OtherControlElement = this->BlueprintBeingCustomized->Hierarchy->Find<FRigControlElement>(ControlElement->GetKey()))
{
OtherControlElement->Settings = ControlElement->Settings;
this->BlueprintBeingCustomized->Hierarchy->Notify(ERigHierarchyNotification::ControlSettingChanged, ControlElement);
this->BlueprintBeingCustomized->Hierarchy->SetControlValue(ControlElement, ValueToSet, ERigControlValueType::Initial, true);
this->BlueprintBeingCustomized->Hierarchy->SetControlValue(ControlElement, ValueToSet, ERigControlValueType::Current, true);
}
}
}
}
));
if(ControlElement->Settings.ControlType == ERigControlType::Bool)
{
DetailBuilder.HideCategory(TEXT("Pose"));
DetailBuilder.HideProperty(TEXT("Offset"));
DetailBuilder.HideProperty(TEXT("Gizmo"));
}
else
{
// setup offset transform
{
const TSharedRef<IPropertyHandle> OffsetInitialLocalTransformHandle = DetailBuilder.GetProperty(TEXT("Offset.Initial.Local.Transform"));
ControlCategory.AddProperty(OffsetInitialLocalTransformHandle).DisplayName(FText::FromString(TEXT("Offset Transform")));
DetailBuilder.HideProperty(TEXT("Offset"));
OffsetPropertyChain.AddHead(FRigControlElement::StaticStruct()->FindPropertyByName(TEXT("Offset")));
OffsetPropertyChain.AddTail(FRigCurrentAndInitialTransform::StaticStruct()->FindPropertyByName(TEXT("Initial")));
OffsetPropertyChain.AddTail(FRigLocalAndGlobalTransform::StaticStruct()->FindPropertyByName(TEXT("Local")));
OffsetPropertyChain.AddTail(FRigComputedTransform::StaticStruct()->FindPropertyByName(TEXT("Transform")));
OffsetPropertyChain.SetActiveMemberPropertyNode(OffsetPropertyChain.GetTail()->GetValue());
const FSimpleDelegate OnTransformChangedDelegate = FSimpleDelegate::CreateStatic(&FRigTransformElementDetails::OnTransformChanged, &OffsetPropertyChain, BlueprintBeingCustomized);
OffsetInitialLocalTransformHandle->SetOnPropertyValueChanged(OnTransformChangedDelegate);
OffsetInitialLocalTransformHandle->SetOnChildPropertyValueChanged(OnTransformChangedDelegate);
OffsetInitialLocalTransformHandle->SetToolTipText(FText::FromString(TEXT("The offset transform is used as a middle man between the parent and the local transform.\nYou can use it to offset a control for visual adjustment.")));
}
// setup gizmo transform
{
const TSharedRef<IPropertyHandle> GizmoInitialLocalTransformHandle = DetailBuilder.GetProperty(TEXT("Gizmo.Initial.Local.Transform"));
GizmoCategory.AddProperty(GizmoInitialLocalTransformHandle).DisplayName(FText::FromString(TEXT("Gizmo Transform")));
DetailBuilder.HideProperty(TEXT("Gizmo"));
GizmoPropertyChain.AddHead(FRigControlElement::StaticStruct()->FindPropertyByName(TEXT("Gizmo")));
GizmoPropertyChain.AddTail(FRigCurrentAndInitialTransform::StaticStruct()->FindPropertyByName(TEXT("Initial")));
GizmoPropertyChain.AddTail(FRigLocalAndGlobalTransform::StaticStruct()->FindPropertyByName(TEXT("Local")));
GizmoPropertyChain.AddTail(FRigComputedTransform::StaticStruct()->FindPropertyByName(TEXT("Transform")));
GizmoPropertyChain.SetActiveMemberPropertyNode(GizmoPropertyChain.GetTail()->GetValue());
const FSimpleDelegate OnTransformChangedDelegate = FSimpleDelegate::CreateStatic(&FRigTransformElementDetails::OnTransformChanged, &GizmoPropertyChain, BlueprintBeingCustomized);
GizmoInitialLocalTransformHandle->SetOnPropertyValueChanged(OnTransformChangedDelegate);
GizmoInitialLocalTransformHandle->SetOnChildPropertyValueChanged(OnTransformChangedDelegate);
GizmoInitialLocalTransformHandle->SetToolTipText(FText::FromString(TEXT("The gizmo transform is used as a transform applied only to the UI element on the screen.\nIt doesn't affect animation.")));
}
}
FRigControlElementDetails_SetupValueWidget(ControlCategory, ERigControlValueType::Current, ControlElement, HierarchyBeingCustomized);
switch (ControlElement->Settings.ControlType)
{
case ERigControlType::Bool:
case ERigControlType::Float:
case ERigControlType::Integer:
case ERigControlType::Vector2D:
{
FRigControlElementDetails_SetupValueWidget(ControlCategory, ERigControlValueType::Initial, ControlElement, HierarchyBeingCustomized);
break;
}
default:
{
break;
}
}
FRigControlElementDetails_SetupValueWidget(LimitsCategory, ERigControlValueType::Minimum, ControlElement, HierarchyBeingCustomized);
FRigControlElementDetails_SetupValueWidget(LimitsCategory, ERigControlValueType::Maximum, ControlElement, HierarchyBeingCustomized);
switch (ControlElement->Settings.ControlType)
{
case ERigControlType::Float:
case ERigControlType::Integer:
case ERigControlType::Vector2D:
case ERigControlType::Position:
case ERigControlType::Scale:
case ERigControlType::Rotator:
case ERigControlType::Transform:
case ERigControlType::TransformNoScale:
case ERigControlType::EulerTransform:
{
const TSharedRef<IPropertyHandle> GizmoNameHandle = DetailBuilder.GetProperty(TEXT("Settings.GizmoName"));
DetailBuilder.HideProperty(GizmoNameHandle);
GizmoCategory.AddProperty(GizmoNameHandle).CustomWidget()
.NameContent()
[
SNew(STextBlock)
.Text(FText::FromString(TEXT("Gizmo Name")))
.Font(IDetailLayoutBuilder::GetDetailFont())
.IsEnabled(this, &FRigControlElementDetails::IsGizmoEnabled)
]
.ValueContent()
[
SNew(SControlRigGizmoNameList, ControlElement, BlueprintBeingCustomized)
.OnGetNameListContent(this, &FRigControlElementDetails::GetGizmoNameList)
.IsEnabled(this, &FRigControlElementDetails::IsGizmoEnabled)
];
break;
}
default:
{
DetailBuilder.HideProperty(TEXT("Settings.bGizmoEnabled"));
DetailBuilder.HideProperty(TEXT("Settings.bGizmoVisible"));
DetailBuilder.HideProperty(TEXT("Settings.GizmoName"));
DetailBuilder.HideProperty(TEXT("Settings.GizmoColor"));
break;
}
}
if(ControlElement->Settings.ControlType != ERigControlType::Integer)
{
DetailBuilder.HideProperty(TEXT("Settings.ControlEnum"));
}
bool bShowLimitTranslation = false;
bool bShowLimitRotation = false;
bool bShowLimitScale = false;
switch (ControlElement->Settings.ControlType)
{
case ERigControlType::Float:
case ERigControlType::Integer:
case ERigControlType::Vector2D:
case ERigControlType::Position:
case ERigControlType::Transform:
case ERigControlType::TransformNoScale:
case ERigControlType::EulerTransform:
{
bShowLimitTranslation = true;
break;
}
default:
{
break;
}
}
switch (ControlElement->Settings.ControlType)
{
case ERigControlType::Rotator:
case ERigControlType::Transform:
case ERigControlType::TransformNoScale:
case ERigControlType::EulerTransform:
{
bShowLimitRotation = true;
break;
}
default:
{
break;
}
}
switch (ControlElement->Settings.ControlType)
{
case ERigControlType::Scale:
case ERigControlType::Transform:
case ERigControlType::EulerTransform:
{
bShowLimitScale = true;
break;
}
default:
{
break;
}
}
if(!bShowLimitTranslation)
{
DetailBuilder.HideProperty(TEXT("Settings.bLimitTranslation"));
}
if(!bShowLimitRotation)
{
DetailBuilder.HideProperty(TEXT("Settings.bLimitRotation"));
}
if(!bShowLimitScale)
{
DetailBuilder.HideProperty(TEXT("Settings.bLimitScale"));
}
switch (ControlElement->Settings.ControlType)
{
case ERigControlType::Integer:
case ERigControlType::Float:
case ERigControlType::Vector2D:
{
break;
}
default:
{
DetailBuilder.HideProperty(TEXT("Settings.PrimaryAxis"));
break;
}
}
if (ControlElement->Settings.ControlType == ERigControlType::Integer)
{
TSharedRef<IPropertyHandle> ControlEnum = DetailBuilder.GetProperty(TEXT("Settings.ControlEnum"));
ControlEnum->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda(
[this, &DetailBuilder]()
{
DetailBuilder.ForceRefreshDetails();
if (this->HierarchyBeingCustomized != nullptr && this->ElementKeyBeingCustomized)
{
if(FRigControlElement* ControlBeingCustomized = this->HierarchyBeingCustomized->Find<FRigControlElement>(ElementKeyBeingCustomized))
{
const UEnum* ControlEnum = ControlBeingCustomized->Settings.ControlEnum;
if (ControlEnum != nullptr)
{
int32 Maximum = (int32)ControlEnum->GetMaxEnumValue() - 1;
ControlBeingCustomized->Settings.MinimumValue.Set<int32>(0);
ControlBeingCustomized->Settings.MaximumValue.Set<int32>(Maximum);
HierarchyBeingCustomized->Notify(ERigHierarchyNotification::ControlSettingChanged, ControlBeingCustomized);
FRigControlValue InitialValue = HierarchyBeingCustomized->GetControlValue(ControlBeingCustomized, ERigControlValueType::Initial);
FRigControlValue CurrentValue = HierarchyBeingCustomized->GetControlValue(ControlBeingCustomized, ERigControlValueType::Current);
ControlBeingCustomized->Settings.ApplyLimits(InitialValue);
ControlBeingCustomized->Settings.ApplyLimits(CurrentValue);
HierarchyBeingCustomized->SetControlValue(ControlBeingCustomized, InitialValue, ERigControlValueType::Initial);
HierarchyBeingCustomized->SetControlValue(ControlBeingCustomized, CurrentValue, ERigControlValueType::Current);
if (UControlRig* DebuggedRig = Cast<UControlRig>(BlueprintBeingCustomized->GetObjectBeingDebugged()))
{
URigHierarchy* DebuggedHierarchy = DebuggedRig->GetHierarchy();
if(FRigControlElement* DebuggedControlElement = DebuggedHierarchy->Find<FRigControlElement>(ElementKeyBeingCustomized))
{
DebuggedControlElement->Settings.MinimumValue.Set<int32>(0);
DebuggedControlElement->Settings.MaximumValue.Set<int32>(Maximum);
DebuggedHierarchy->Notify(ERigHierarchyNotification::ControlSettingChanged, DebuggedControlElement);
DebuggedHierarchy->SetControlValue(DebuggedControlElement, InitialValue, ERigControlValueType::Initial);
DebuggedHierarchy->SetControlValue(DebuggedControlElement, CurrentValue, ERigControlValueType::Current);
}
}
}
}
}
}
));
}
}
FText FRigControlElementDetails::GetDisplayName() const
{
if (HierarchyBeingCustomized != nullptr && ElementKeyBeingCustomized)
{
if(FRigControlElement* ControlElement = HierarchyBeingCustomized->Find<FRigControlElement>(ElementKeyBeingCustomized))
{
if (ControlElement->Settings.DisplayName.IsNone())
{
return FText();
}
return FText::FromName(ControlElement->GetDisplayName());
}
}
return FText();
}
void FRigControlElementDetails::SetDisplayName(const FText& InNewText, ETextCommit::Type InCommitType, const TSharedRef<IPropertyUtilities> PropertyUtilities)
{
if (HierarchyBeingCustomized != nullptr && ElementKeyBeingCustomized)
{
if(FRigControlElement* ControlElement = HierarchyBeingCustomized->Find<FRigControlElement>(ElementKeyBeingCustomized))
{
if(BlueprintBeingCustomized)
{
BlueprintBeingCustomized->Hierarchy->Modify();
}
const FString NewDisplayName = InNewText.ToString().TrimStartAndEnd();
if (NewDisplayName.IsEmpty())
{
ControlElement->Settings.DisplayName = FName(NAME_None);
}
else
{
ControlElement->Settings.DisplayName = *NewDisplayName;
}
HierarchyBeingCustomized->Notify(ERigHierarchyNotification::ControlSettingChanged, ControlElement);
if (BlueprintBeingCustomized && BlueprintBeingCustomized->Hierarchy != HierarchyBeingCustomized)
{
if(FRigControlElement* OtherControlElement = BlueprintBeingCustomized->Hierarchy->Find<FRigControlElement>(ElementKeyBeingCustomized))
{
OtherControlElement->Settings.DisplayName = ControlElement->Settings.DisplayName;
BlueprintBeingCustomized->Hierarchy->Notify(ERigHierarchyNotification::ControlSettingChanged, OtherControlElement);
}
}
}
}
}
bool FRigControlElementDetails::IsGizmoEnabled() const
{
URigHierarchy* Hierarchy = HierarchyBeingCustomized;
if (Hierarchy != nullptr && ElementKeyBeingCustomized)
{
if(FRigControlElement* ControlElement = Hierarchy->Find<FRigControlElement>(ElementKeyBeingCustomized))
{
return ControlElement->Settings.bGizmoEnabled;
}
}
return false;
}
bool FRigControlElementDetails::IsEnabled(ERigControlValueType InValueType) const
{
switch (InValueType)
{
case ERigControlValueType::Minimum:
case ERigControlValueType::Maximum:
{
if (HierarchyBeingCustomized != nullptr && ElementKeyBeingCustomized)
{
if(FRigControlElement* ControlElement = HierarchyBeingCustomized->Find<FRigControlElement>(ElementKeyBeingCustomized))
{
return ControlElement->Settings.bLimitTranslation || ControlElement->Settings.bLimitRotation || ControlElement->Settings.bLimitScale;
}
}
return false;
}
default:
{
break;
}
}
return true;
}
const TArray<TSharedPtr<FString>>& FRigControlElementDetails::GetGizmoNameList() const
{
return GizmoNameList;
}
const TArray<TSharedPtr<FString>>& FRigControlElementDetails::GetControlTypeList() const
{
return ControlTypeList;
}
void FRigNullElementDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
FRigTransformElementDetails::CustomizeDetails(DetailBuilder);
}
#undef LOCTEXT_NAMESPACE