You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Removed implicit casts between FEulerTransform and FTransform - moved interrogator and tokens inside of sequencer to use FEulerTransform - added the notion of a preferred rotator for Controls. transform takes precedence for runtime solves, but UI / sequencer relies on the preferred rotator #rb mike.zyracki benoit.gadreau #jira UE-150830 #preflight https://horde.devtools.epicgames.com/job/62875a58286cf1867a419989 [CL 20298666 by Helge Mathee in ue5-main branch]
3864 lines
122 KiB
C++
3864 lines
122 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ControlRigElementDetails.h"
|
|
#include "Widgets/SWidget.h"
|
|
#include "IDetailChildrenBuilder.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Widgets/Input/SEditableTextBox.h"
|
|
#include "Widgets/Input/SVectorInputBox.h"
|
|
#include "Widgets/Input/SCheckBox.h"
|
|
#include "Widgets/Input/SButton.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"
|
|
#include "HAL/PlatformApplicationMisc.h"
|
|
#include "Styling/AppStyle.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "ControlRigElementDetails"
|
|
|
|
static const FText ControlRigDetailsMultipleValues = LOCTEXT("MultipleValues", "Multiple Values");
|
|
|
|
struct FRigElementTransformWidgetSettings
|
|
{
|
|
FRigElementTransformWidgetSettings()
|
|
: RotationRepresentation(MakeShareable(new ESlateRotationRepresentation::Type(ESlateRotationRepresentation::Rotator)))
|
|
, IsComponentRelative(MakeShareable(new UE::Math::TVector<float>(1.f, 1.f, 1.f)))
|
|
, IsScaleLocked(TSharedPtr<bool>(new bool(false)))
|
|
{
|
|
}
|
|
|
|
TSharedPtr<ESlateRotationRepresentation::Type> RotationRepresentation;
|
|
TSharedRef<UE::Math::TVector<float>> IsComponentRelative;
|
|
TSharedPtr<bool> IsScaleLocked;
|
|
|
|
static FRigElementTransformWidgetSettings& FindOrAdd(
|
|
ERigControlValueType InValueType,
|
|
ERigTransformElementDetailsTransform::Type InTransformType,
|
|
const SAdvancedTransformInputBox<FEulerTransform>::FArguments& WidgetArgs)
|
|
{
|
|
uint32 Hash = GetTypeHash(WidgetArgs._ConstructLocation);
|
|
Hash = HashCombine(Hash, GetTypeHash(WidgetArgs._ConstructRotation));
|
|
Hash = HashCombine(Hash, GetTypeHash(WidgetArgs._ConstructScale));
|
|
Hash = HashCombine(Hash, GetTypeHash(WidgetArgs._AllowEditRotationRepresentation));
|
|
Hash = HashCombine(Hash, GetTypeHash(WidgetArgs._DisplayScaleLock));
|
|
Hash = HashCombine(Hash, GetTypeHash(InValueType));
|
|
Hash = HashCombine(Hash, GetTypeHash(InTransformType));
|
|
return sSettings.FindOrAdd(Hash);
|
|
}
|
|
|
|
static TMap<uint32, FRigElementTransformWidgetSettings> sSettings;
|
|
};
|
|
|
|
TMap<uint32, FRigElementTransformWidgetSettings> FRigElementTransformWidgetSettings::sSettings;
|
|
|
|
|
|
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 = CastChecked<UControlRigBlueprint>(Object);
|
|
break;
|
|
}
|
|
|
|
OutBlueprint = Object->GetTypedOuter<UControlRigBlueprint>();
|
|
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))
|
|
{
|
|
UControlRigBlueprint* Blueprint = Cast<UControlRigBlueprint>(Rig->GetClass()->ClassGeneratedBy);
|
|
if (Blueprint)
|
|
{
|
|
if(Blueprint->GetOutermost() == Package)
|
|
{
|
|
OutBlueprint = Blueprint;
|
|
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)
|
|
.IsEnabled(!NameHandle->IsEditConst())
|
|
.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( FAppStyle::Get(), "NoBorder" )
|
|
.ButtonColorAndOpacity_Lambda([this]() { return OnGetWidgetBackground(UseSelectedButton); })
|
|
.OnClicked(this, &FRigElementKeyDetails::OnGetSelectedClicked)
|
|
.ContentPadding(1.f)
|
|
.ToolTipText(NSLOCTEXT("ControlRigElementDetails", "ObjectGraphPin_Use_Tooltip", "Use item selected"))
|
|
[
|
|
SNew(SImage)
|
|
.ColorAndOpacity_Lambda( [this]() { return OnGetWidgetForeground(UseSelectedButton); })
|
|
.Image(FAppStyle::GetBrush("Icons.CircleArrowLeft"))
|
|
]
|
|
]
|
|
// Select in hierarchy button
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(1,0)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SAssignNew(SelectElementButton, SButton)
|
|
.ButtonStyle( FAppStyle::Get(), "NoBorder" )
|
|
.ButtonColorAndOpacity_Lambda([this]() { return OnGetWidgetBackground(SelectElementButton); })
|
|
.OnClicked(this, &FRigElementKeyDetails::OnSelectInHierarchyClicked)
|
|
.ContentPadding(0)
|
|
.ToolTipText(NSLOCTEXT("ControlRigElementDetails", "ObjectGraphPin_Browse_Tooltip", "Select in hierarchy"))
|
|
[
|
|
SNew(SImage)
|
|
.ColorAndOpacity_Lambda( [this]() { return OnGetWidgetForeground(SelectElementButton); })
|
|
.Image(FAppStyle::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())
|
|
{
|
|
for(int32 ObjectIndex = 0; ObjectIndex < NameHandle->GetNumPerObjectValues(); ObjectIndex++)
|
|
{
|
|
FString PerObjectValue;
|
|
NameHandle->GetPerObjectValue(ObjectIndex, PerObjectValue);
|
|
|
|
if(ObjectIndex == 0)
|
|
{
|
|
ElementNameStr = PerObjectValue;
|
|
}
|
|
else if(ElementNameStr != PerObjectValue)
|
|
{
|
|
return ControlRigDetailsMultipleValues.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 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("Shape.")))
|
|
{
|
|
PropertyPath.RightChopInline(6);
|
|
PropertyChain.AddTail(FRigControlElement::StaticStruct()->FindPropertyByName(TEXT("Shape")));
|
|
}
|
|
|
|
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)
|
|
{
|
|
BlueprintBeingCustomized = nullptr;
|
|
HierarchyBeingCustomized = nullptr;
|
|
ObjectsBeingCustomized.Reset();
|
|
|
|
TArray<TWeakObjectPtr<UObject>> DetailObjects;
|
|
DetailBuilder.GetObjectsBeingCustomized(DetailObjects);
|
|
for(TWeakObjectPtr<UObject> DetailObject : DetailObjects)
|
|
{
|
|
UDetailsViewWrapperObject* WrapperObject = CastChecked<UDetailsViewWrapperObject>(DetailObject.Get());
|
|
|
|
if(HierarchyBeingCustomized == nullptr)
|
|
{
|
|
HierarchyBeingCustomized = Cast<URigHierarchy>(WrapperObject->GetOuter());
|
|
}
|
|
|
|
ObjectsBeingCustomized.Add(WrapperObject);
|
|
}
|
|
|
|
if(HierarchyBeingCustomized)
|
|
{
|
|
BlueprintBeingCustomized = HierarchyBeingCustomized->GetTypedOuter<UControlRigBlueprint>();
|
|
if(BlueprintBeingCustomized == nullptr)
|
|
{
|
|
if(UControlRig* ControlRig = HierarchyBeingCustomized->GetTypedOuter<UControlRig>())
|
|
{
|
|
BlueprintBeingCustomized = Cast<UControlRigBlueprint>(ControlRig->GetClass()->ClassGeneratedBy);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(BlueprintBeingCustomized == nullptr)
|
|
{
|
|
RigElementDetails_GetCustomizedInfo(DetailBuilder.GetProperty(TEXT("Key")), BlueprintBeingCustomized);
|
|
}
|
|
|
|
IDetailCategoryBuilder& GeneralCategory = DetailBuilder.EditCategory(TEXT("General"), LOCTEXT("General", "General"));
|
|
|
|
GeneralCategory.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)
|
|
.OnVerifyTextChanged(this, &FRigBaseElementDetails::OnVerifyNameChanged)
|
|
.IsEnabled(ObjectsBeingCustomized.Num() == 1)
|
|
];
|
|
|
|
DetailBuilder.HideCategory(TEXT("RigElement"));
|
|
}
|
|
|
|
URigHierarchy* FRigBaseElementDetails::GetHierarchyBeingDebugged() const
|
|
{
|
|
URigHierarchy* HierarchyBeingDebugged = HierarchyBeingCustomized;
|
|
|
|
if (UControlRig* DebuggedRig = Cast<UControlRig>(BlueprintBeingCustomized->GetObjectBeingDebugged()))
|
|
{
|
|
if(!DebuggedRig->IsSetupModeEnabled())
|
|
{
|
|
HierarchyBeingDebugged = DebuggedRig->GetHierarchy();
|
|
}
|
|
}
|
|
|
|
return HierarchyBeingDebugged;
|
|
}
|
|
|
|
FRigElementKey FRigBaseElementDetails::GetElementKey() const
|
|
{
|
|
check(ObjectsBeingCustomized.Num() == 1);
|
|
if(ObjectsBeingCustomized[0].IsValid())
|
|
{
|
|
return ObjectsBeingCustomized[0]->GetContent<FRigBaseElement>().GetKey();
|
|
}
|
|
return FRigElementKey();
|
|
}
|
|
|
|
FText FRigBaseElementDetails::GetName() const
|
|
{
|
|
if(ObjectsBeingCustomized.Num() > 1)
|
|
{
|
|
return ControlRigDetailsMultipleValues;
|
|
}
|
|
return FText::FromName(GetElementKey().Name);
|
|
}
|
|
|
|
void FRigBaseElementDetails::SetName(const FText& InNewText, ETextCommit::Type InCommitType)
|
|
{
|
|
if(InCommitType == ETextCommit::OnCleared)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(ObjectsBeingCustomized.Num() > 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
URigHierarchy* Hierarchy = nullptr;
|
|
if (BlueprintBeingCustomized)
|
|
{
|
|
Hierarchy = BlueprintBeingCustomized->Hierarchy;
|
|
}
|
|
else
|
|
{
|
|
Hierarchy = GetHierarchy();
|
|
}
|
|
|
|
if (Hierarchy)
|
|
{
|
|
BeginDestroy();
|
|
|
|
URigHierarchyController* Controller = Hierarchy->GetController(true);
|
|
check(Controller);
|
|
Controller->RenameElement(GetElementKey(), *InNewText.ToString(), true, true);
|
|
}
|
|
}
|
|
|
|
bool FRigBaseElementDetails::OnVerifyNameChanged(const FText& InText, FText& OutErrorMessage)
|
|
{
|
|
if(ObjectsBeingCustomized.Num() > 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
URigHierarchy* Hierarchy = nullptr;
|
|
if (BlueprintBeingCustomized)
|
|
{
|
|
Hierarchy = BlueprintBeingCustomized->Hierarchy;
|
|
}
|
|
else
|
|
{
|
|
Hierarchy = GetHierarchy();
|
|
}
|
|
|
|
if (!Hierarchy)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (GetElementKey().Name.ToString() == InText.ToString())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FString OutErrorMessageStr;
|
|
if (!Hierarchy->IsNameAvailable(InText.ToString(), GetElementKey().Type, &OutErrorMessageStr))
|
|
{
|
|
OutErrorMessage = FText::FromString(OutErrorMessageStr);
|
|
return false;
|
|
}
|
|
|
|
return 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;
|
|
}
|
|
|
|
TArray<FRigElementKey> FRigBaseElementDetails::GetElementKeys() const
|
|
{
|
|
TArray<FRigElementKey> Keys;
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
Keys.Add(ObjectBeingCustomized->GetContent<FRigBaseElement>().GetKey());
|
|
}
|
|
}
|
|
return Keys;
|
|
}
|
|
|
|
bool FRigBaseElementDetails::IsAnyElementOfType(ERigElementType InType) const
|
|
{
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->GetContent<FRigBaseElement>().GetKey().Type == InType)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FRigBaseElementDetails::IsAnyElementNotOfType(ERigElementType InType) const
|
|
{
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->GetContent<FRigBaseElement>().GetKey().Type != InType)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FRigBaseElementDetails::IsAnyControlOfAnimationType(ERigControlAnimationType InType) const
|
|
{
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigControlElement>())
|
|
{
|
|
const FRigControlElement ControlElement = ObjectBeingCustomized->GetContent<FRigControlElement>();
|
|
if(ControlElement.Settings.AnimationType == InType)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FRigBaseElementDetails::IsAnyControlNotOfAnimationType(ERigControlAnimationType InType) const
|
|
{
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigControlElement>())
|
|
{
|
|
const FRigControlElement ControlElement = ObjectBeingCustomized->GetContent<FRigControlElement>();
|
|
if(ControlElement.Settings.AnimationType != InType)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FRigBaseElementDetails::IsAnyControlOfValueType(ERigControlType InType) const
|
|
{
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigControlElement>())
|
|
{
|
|
const FRigControlElement ControlElement = ObjectBeingCustomized->GetContent<FRigControlElement>();
|
|
if(ControlElement.Settings.ControlType == InType)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FRigBaseElementDetails::IsAnyControlNotOfValueType(ERigControlType InType) const
|
|
{
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigControlElement>())
|
|
{
|
|
const FRigControlElement ControlElement = ObjectBeingCustomized->GetContent<FRigControlElement>();
|
|
if(ControlElement.Settings.ControlType != InType)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FRigBaseElementDetails::RegisterSectionMappings(FPropertyEditorModule& PropertyEditorModule)
|
|
{
|
|
FRigBoneElementDetails().RegisterSectionMappings(PropertyEditorModule, UDetailsViewWrapperObject::GetClassForStruct(FRigBoneElement::StaticStruct()));
|
|
FRigNullElementDetails().RegisterSectionMappings(PropertyEditorModule, UDetailsViewWrapperObject::GetClassForStruct(FRigNullElement::StaticStruct()));
|
|
FRigControlElementDetails().RegisterSectionMappings(PropertyEditorModule, UDetailsViewWrapperObject::GetClassForStruct(FRigControlElement::StaticStruct()));
|
|
}
|
|
|
|
void FRigBaseElementDetails::RegisterSectionMappings(FPropertyEditorModule& PropertyEditorModule, UClass* InClass)
|
|
{
|
|
}
|
|
|
|
TSharedPtr<TArray<ERigTransformElementDetailsTransform::Type>> FRigTransformElementDetails::PickedTransforms;
|
|
|
|
void FRigTransformElementDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
FRigBaseElementDetails::CustomizeDetails(DetailBuilder);
|
|
}
|
|
|
|
void FRigTransformElementDetails::RegisterSectionMappings(FPropertyEditorModule& PropertyEditorModule, UClass* InClass)
|
|
{
|
|
FRigBaseElementDetails::RegisterSectionMappings(PropertyEditorModule, InClass);
|
|
|
|
TSharedRef<FPropertySection> TransformSection = PropertyEditorModule.FindOrCreateSection(InClass->GetFName(), "Transform", LOCTEXT("Transform", "Transform"));
|
|
TransformSection->AddCategory("General");
|
|
TransformSection->AddCategory("Value");
|
|
TransformSection->AddCategory("Transform");
|
|
}
|
|
|
|
void FRigTransformElementDetails::CustomizeTransform(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
URigHierarchy* HierarchyBeingDebugged = HierarchyBeingCustomized;
|
|
if (UControlRig* DebuggedRig = Cast<UControlRig>(BlueprintBeingCustomized->GetObjectBeingDebugged()))
|
|
{
|
|
if(!DebuggedRig->IsSetupModeEnabled())
|
|
{
|
|
HierarchyBeingDebugged = DebuggedRig->GetHierarchy();
|
|
}
|
|
}
|
|
|
|
TArray<FRigElementKey> Keys = GetElementKeys();
|
|
Keys = HierarchyBeingDebugged->SortKeys(Keys);
|
|
const bool bAllControls = !IsAnyElementNotOfType(ERigElementType::Control) && !IsAnyControlOfValueType(ERigControlType::Bool);
|
|
bool bShowLimits = false;
|
|
TArray<ERigTransformElementDetailsTransform::Type> TransformTypes;
|
|
TArray<FText> ButtonLabels;
|
|
TArray<FText> ButtonTooltips;
|
|
|
|
if(bAllControls)
|
|
{
|
|
TransformTypes = {
|
|
ERigTransformElementDetailsTransform::Initial,
|
|
ERigTransformElementDetailsTransform::Current,
|
|
ERigTransformElementDetailsTransform::Offset
|
|
};
|
|
ButtonLabels = {
|
|
LOCTEXT("Initial", "Initial"),
|
|
LOCTEXT("Current", "Current"),
|
|
LOCTEXT("Offset", "Offset")
|
|
};
|
|
ButtonTooltips = {
|
|
LOCTEXT("InitialTooltip", "Initial transform in the reference pose"),
|
|
LOCTEXT("CurrentTooltip", "Current animation transform"),
|
|
LOCTEXT("OffsetTooltip", "Offset transform under the control")
|
|
};
|
|
|
|
bShowLimits = !IsAnyControlNotOfValueType(ERigControlType::EulerTransform);
|
|
|
|
if(bShowLimits)
|
|
{
|
|
TransformTypes.Append({
|
|
ERigTransformElementDetailsTransform::Minimum,
|
|
ERigTransformElementDetailsTransform::Maximum
|
|
});
|
|
ButtonLabels.Append({
|
|
LOCTEXT("Min", "Min"),
|
|
LOCTEXT("Max", "Max")
|
|
});
|
|
ButtonTooltips.Append({
|
|
LOCTEXT("ValueMinimumTooltip", "The minimum limit(s) for the control"),
|
|
LOCTEXT("ValueMaximumTooltip", "The maximum limit(s) for the control")
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TransformTypes = {
|
|
ERigTransformElementDetailsTransform::Initial,
|
|
ERigTransformElementDetailsTransform::Current
|
|
};
|
|
ButtonLabels = {
|
|
LOCTEXT("Initial", "Initial"),
|
|
LOCTEXT("Current", "Current")
|
|
};
|
|
ButtonTooltips = {
|
|
LOCTEXT("InitialTooltip", "Initial transform in the reference pose"),
|
|
LOCTEXT("CurrentTooltip", "Current animation transform")
|
|
};
|
|
}
|
|
|
|
TArray<bool> bTransformsEnabled;
|
|
|
|
// determine if the transforms are enabled
|
|
for(int32 Index = 0; Index < TransformTypes.Num(); Index++)
|
|
{
|
|
const ERigTransformElementDetailsTransform::Type CurrentTransformType = TransformTypes[Index];
|
|
|
|
bool bIsTransformEnabled = true;
|
|
if (IsAnyElementOfType(ERigElementType::Control))
|
|
{
|
|
bIsTransformEnabled = IsAnyControlOfValueType(ERigControlType::EulerTransform) ||
|
|
IsAnyControlOfValueType(ERigControlType::Transform) ||
|
|
CurrentTransformType == ERigTransformElementDetailsTransform::Offset;
|
|
|
|
if(!bIsTransformEnabled)
|
|
{
|
|
ButtonTooltips[Index] = FText::FromString(
|
|
FString::Printf(TEXT("%s\n%s"),
|
|
*ButtonTooltips[Index].ToString(),
|
|
TEXT("Only transform controls can be edited here. Refer to the 'Value' section instead.")));
|
|
}
|
|
}
|
|
else if (IsAnyElementOfType(ERigElementType::Bone) && CurrentTransformType == ERigTransformElementDetailsTransform::Initial)
|
|
{
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigBoneElement>())
|
|
{
|
|
const FRigBoneElement BoneElement = ObjectBeingCustomized->GetContent<FRigBoneElement>();
|
|
bIsTransformEnabled = BoneElement.BoneType == ERigBoneType::User;
|
|
|
|
if(!bIsTransformEnabled)
|
|
{
|
|
ButtonTooltips[Index] = FText::FromString(
|
|
FString::Printf(TEXT("%s\n%s"),
|
|
*ButtonTooltips[Index].ToString(),
|
|
TEXT("Imported Bones' initial transform cannot be edited.")));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bTransformsEnabled.Add(bIsTransformEnabled);
|
|
}
|
|
|
|
if(!PickedTransforms.IsValid())
|
|
{
|
|
PickedTransforms = MakeShareable(new TArray<ERigTransformElementDetailsTransform::Type>({ERigTransformElementDetailsTransform::Current}));
|
|
}
|
|
|
|
TSharedPtr<SSegmentedControl<ERigTransformElementDetailsTransform::Type>> TransformChoiceWidget =
|
|
SSegmentedControl<ERigTransformElementDetailsTransform::Type>::Create(
|
|
TransformTypes,
|
|
ButtonLabels,
|
|
ButtonTooltips,
|
|
*PickedTransforms.Get(),
|
|
true,
|
|
SSegmentedControl<ERigTransformElementDetailsTransform::Type>::FOnValuesChanged::CreateLambda(
|
|
[](TArray<ERigTransformElementDetailsTransform::Type> NewSelection)
|
|
{
|
|
(*FRigTransformElementDetails::PickedTransforms.Get()) = NewSelection;
|
|
}
|
|
)
|
|
);
|
|
|
|
IDetailCategoryBuilder& TransformCategory = DetailBuilder.EditCategory(TEXT("Transform"), LOCTEXT("Transform", "Transform"));
|
|
AddChoiceWidgetRow(TransformCategory, FText::FromString(TEXT("TransformType")), TransformChoiceWidget.ToSharedRef());
|
|
|
|
|
|
SAdvancedTransformInputBox<FEulerTransform>::FArguments TransformWidgetArgs = SAdvancedTransformInputBox<FEulerTransform>::FArguments()
|
|
.DisplayToggle(false)
|
|
.DisplayRelativeWorld(true)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont());
|
|
|
|
for(int32 Index = 0; Index < ButtonLabels.Num(); Index++)
|
|
{
|
|
const ERigTransformElementDetailsTransform::Type CurrentTransformType = TransformTypes[Index];
|
|
ERigControlValueType CurrentValueType = ERigControlValueType::Current;
|
|
switch(CurrentTransformType)
|
|
{
|
|
case ERigTransformElementDetailsTransform::Initial:
|
|
{
|
|
CurrentValueType = ERigControlValueType::Initial;
|
|
break;
|
|
}
|
|
case ERigTransformElementDetailsTransform::Minimum:
|
|
{
|
|
CurrentValueType = ERigControlValueType::Minimum;
|
|
break;
|
|
}
|
|
case ERigTransformElementDetailsTransform::Maximum:
|
|
{
|
|
CurrentValueType = ERigControlValueType::Maximum;
|
|
break;
|
|
}
|
|
}
|
|
|
|
TransformWidgetArgs.Visibility_Lambda([TransformChoiceWidget, Index]() -> EVisibility
|
|
{
|
|
return TransformChoiceWidget->HasValue((ERigTransformElementDetailsTransform::Type)Index) ? EVisibility::Visible : EVisibility::Collapsed;
|
|
});
|
|
|
|
TransformWidgetArgs.IsEnabled(bTransformsEnabled[Index]);
|
|
|
|
CreateEulerTransformValueWidgetRow(
|
|
HierarchyBeingDebugged,
|
|
Keys,
|
|
TransformWidgetArgs,
|
|
TransformCategory,
|
|
ButtonLabels[Index],
|
|
ButtonTooltips[Index],
|
|
CurrentTransformType,
|
|
CurrentValueType);
|
|
}
|
|
}
|
|
|
|
bool FRigTransformElementDetails::IsCurrentLocalEnabled() const
|
|
{
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->GetContent<FRigBaseElement>().GetType() == ERigElementType::Control)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FRigTransformElementDetails::AddChoiceWidgetRow(IDetailCategoryBuilder& InCategory, const FText& InSearchText, TSharedRef<SWidget> InWidget)
|
|
{
|
|
InCategory.AddCustomRow(FText::FromString(TEXT("TransformType")))
|
|
.ValueContent()
|
|
.MinDesiredWidth(375.f)
|
|
.MaxDesiredWidth(375.f)
|
|
.HAlign(HAlign_Left)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Left)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
InWidget
|
|
]
|
|
];
|
|
}
|
|
|
|
TSharedPtr<TArray<ERigControlValueType>> FRigControlElementDetails::PickedValueTypes;
|
|
|
|
void FRigTransformElementDetails::CreateEulerTransformValueWidgetRow(
|
|
URigHierarchy* HierarchyBeingDebugged,
|
|
const TArray<FRigElementKey>& Keys,
|
|
SAdvancedTransformInputBox<FEulerTransform>::FArguments TransformWidgetArgs,
|
|
IDetailCategoryBuilder& CategoryBuilder,
|
|
const FText& Label,
|
|
const FText& Tooltip,
|
|
ERigTransformElementDetailsTransform::Type CurrentTransformType,
|
|
ERigControlValueType ValueType)
|
|
{
|
|
const FRigElementTransformWidgetSettings& Settings = FRigElementTransformWidgetSettings::FindOrAdd(ValueType, CurrentTransformType, TransformWidgetArgs);
|
|
TSharedRef<UE::Math::TVector<float>> IsComponentRelativeStorage = Settings.IsComponentRelative;
|
|
|
|
TransformWidgetArgs.OnGetIsComponentRelative_Lambda(
|
|
[IsComponentRelativeStorage](ESlateTransformComponent::Type InComponent)
|
|
{
|
|
return IsComponentRelativeStorage->operator[]((int32)InComponent) > 0.f;
|
|
})
|
|
.OnIsComponentRelativeChanged_Lambda(
|
|
[IsComponentRelativeStorage](ESlateTransformComponent::Type InComponent, bool bIsRelative)
|
|
{
|
|
IsComponentRelativeStorage->operator[]((int32)InComponent) = bIsRelative ? 1.f : 0.f;
|
|
});
|
|
|
|
const TSharedPtr<ESlateRotationRepresentation::Type> RotationRepresentationStorage = Settings.RotationRepresentation;
|
|
TransformWidgetArgs.RotationRepresentation(RotationRepresentationStorage);
|
|
|
|
auto IsComponentRelative = [TransformWidgetArgs](int32 Component) -> bool
|
|
{
|
|
if(TransformWidgetArgs._OnGetIsComponentRelative.IsBound())
|
|
{
|
|
return TransformWidgetArgs._OnGetIsComponentRelative.Execute((ESlateTransformComponent::Type)Component);
|
|
}
|
|
return true;
|
|
};
|
|
|
|
auto ConformComponentRelative = [TransformWidgetArgs, IsComponentRelative](int32 Component)
|
|
{
|
|
if(TransformWidgetArgs._OnIsComponentRelativeChanged.IsBound())
|
|
{
|
|
bool bRelative = IsComponentRelative(Component);
|
|
TransformWidgetArgs._OnIsComponentRelativeChanged.Execute(ESlateTransformComponent::Location, bRelative);
|
|
TransformWidgetArgs._OnIsComponentRelativeChanged.Execute(ESlateTransformComponent::Rotation, bRelative);
|
|
TransformWidgetArgs._OnIsComponentRelativeChanged.Execute(ESlateTransformComponent::Scale, bRelative);
|
|
}
|
|
};
|
|
|
|
TransformWidgetArgs.IsScaleLocked(Settings.IsScaleLocked);
|
|
|
|
switch(CurrentTransformType)
|
|
{
|
|
case ERigTransformElementDetailsTransform::Minimum:
|
|
case ERigTransformElementDetailsTransform::Maximum:
|
|
{
|
|
TransformWidgetArgs.AllowEditRotationRepresentation(false);
|
|
TransformWidgetArgs.DisplayRelativeWorld(false);
|
|
TransformWidgetArgs.DisplayToggle(true);
|
|
TransformWidgetArgs.OnGetToggleChecked_Lambda([Keys, HierarchyBeingDebugged, ValueType]
|
|
(
|
|
ESlateTransformComponent::Type Component,
|
|
ESlateRotationRepresentation::Type RotationRepresentation,
|
|
ESlateTransformSubComponent::Type SubComponent
|
|
) -> ECheckBoxState
|
|
{
|
|
TOptional<bool> FirstValue;
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
if(const FRigControlElement* ControlElement = HierarchyBeingDebugged->Find<FRigControlElement>(Key))
|
|
{
|
|
TOptional<bool> Value;
|
|
|
|
switch(ControlElement->Settings.ControlType)
|
|
{
|
|
case ERigControlType::Position:
|
|
case ERigControlType::Rotator:
|
|
case ERigControlType::Scale:
|
|
{
|
|
if(ControlElement->Settings.LimitEnabled.Num() == 3)
|
|
{
|
|
const int32 Index = ControlElement->Settings.ControlType == ERigControlType::Rotator ?
|
|
int32(SubComponent) - int32(ESlateTransformSubComponent::Pitch) :
|
|
int32(SubComponent) - int32(ESlateTransformSubComponent::X);
|
|
|
|
Value = ControlElement->Settings.LimitEnabled[Index].GetForValueType(ValueType);
|
|
}
|
|
break;
|
|
}
|
|
case ERigControlType::EulerTransform:
|
|
{
|
|
if(ControlElement->Settings.LimitEnabled.Num() == 9)
|
|
{
|
|
switch(Component)
|
|
{
|
|
case ESlateTransformComponent::Location:
|
|
{
|
|
switch(SubComponent)
|
|
{
|
|
case ESlateTransformSubComponent::X:
|
|
{
|
|
Value = ControlElement->Settings.LimitEnabled[0].GetForValueType(ValueType);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Y:
|
|
{
|
|
Value = ControlElement->Settings.LimitEnabled[1].GetForValueType(ValueType);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Z:
|
|
{
|
|
Value = ControlElement->Settings.LimitEnabled[2].GetForValueType(ValueType);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Rotation:
|
|
{
|
|
switch(SubComponent)
|
|
{
|
|
case ESlateTransformSubComponent::Pitch:
|
|
{
|
|
Value = ControlElement->Settings.LimitEnabled[3].GetForValueType(ValueType);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Yaw:
|
|
{
|
|
Value = ControlElement->Settings.LimitEnabled[4].GetForValueType(ValueType);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Roll:
|
|
{
|
|
Value = ControlElement->Settings.LimitEnabled[5].GetForValueType(ValueType);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Scale:
|
|
{
|
|
switch(SubComponent)
|
|
{
|
|
case ESlateTransformSubComponent::X:
|
|
{
|
|
Value = ControlElement->Settings.LimitEnabled[6].GetForValueType(ValueType);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Y:
|
|
{
|
|
Value = ControlElement->Settings.LimitEnabled[7].GetForValueType(ValueType);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Z:
|
|
{
|
|
Value = ControlElement->Settings.LimitEnabled[8].GetForValueType(ValueType);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(Value.IsSet())
|
|
{
|
|
if(FirstValue.IsSet())
|
|
{
|
|
if(FirstValue.GetValue() != Value.GetValue())
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FirstValue = Value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!ensure(FirstValue.IsSet()))
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
return FirstValue.GetValue() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
});
|
|
|
|
TransformWidgetArgs.OnToggleChanged_Lambda([ValueType, Keys, this]
|
|
(
|
|
ESlateTransformComponent::Type Component,
|
|
ESlateRotationRepresentation::Type RotationRepresentation,
|
|
ESlateTransformSubComponent::Type SubComponent,
|
|
ECheckBoxState CheckState
|
|
)
|
|
{
|
|
if(CheckState == ECheckBoxState::Undetermined)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const bool Value = CheckState == ECheckBoxState::Checked;
|
|
|
|
FScopedTransaction Transaction(LOCTEXT("ChangeLimitToggle", "Change Limit Toggle"));
|
|
HierarchyBeingCustomized->Modify();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
if(FRigControlElement* ControlElement = HierarchyBeingCustomized->Find<FRigControlElement>(Key))
|
|
{
|
|
switch(ControlElement->Settings.ControlType)
|
|
{
|
|
case ERigControlType::Position:
|
|
case ERigControlType::Rotator:
|
|
case ERigControlType::Scale:
|
|
{
|
|
if(ControlElement->Settings.LimitEnabled.Num() == 3)
|
|
{
|
|
const int32 Index = ControlElement->Settings.ControlType == ERigControlType::Rotator ?
|
|
int32(SubComponent) - int32(ESlateTransformSubComponent::Pitch) :
|
|
int32(SubComponent) - int32(ESlateTransformSubComponent::X);
|
|
|
|
ControlElement->Settings.LimitEnabled[Index].SetForValueType(ValueType, Value);
|
|
}
|
|
break;
|
|
}
|
|
case ERigControlType::EulerTransform:
|
|
{
|
|
if(ControlElement->Settings.LimitEnabled.Num() == 9)
|
|
{
|
|
switch(Component)
|
|
{
|
|
case ESlateTransformComponent::Location:
|
|
{
|
|
switch(SubComponent)
|
|
{
|
|
case ESlateTransformSubComponent::X:
|
|
{
|
|
ControlElement->Settings.LimitEnabled[0].SetForValueType(ValueType, Value);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Y:
|
|
{
|
|
ControlElement->Settings.LimitEnabled[1].SetForValueType(ValueType, Value);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Z:
|
|
{
|
|
ControlElement->Settings.LimitEnabled[2].SetForValueType(ValueType, Value);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Rotation:
|
|
{
|
|
switch(SubComponent)
|
|
{
|
|
case ESlateTransformSubComponent::Pitch:
|
|
{
|
|
ControlElement->Settings.LimitEnabled[3].SetForValueType(ValueType, Value);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Yaw:
|
|
{
|
|
ControlElement->Settings.LimitEnabled[4].SetForValueType(ValueType, Value);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Roll:
|
|
{
|
|
ControlElement->Settings.LimitEnabled[5].SetForValueType(ValueType, Value);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Scale:
|
|
{
|
|
switch(SubComponent)
|
|
{
|
|
case ESlateTransformSubComponent::X:
|
|
{
|
|
ControlElement->Settings.LimitEnabled[6].SetForValueType(ValueType, Value);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Y:
|
|
{
|
|
ControlElement->Settings.LimitEnabled[7].SetForValueType(ValueType, Value);
|
|
break;
|
|
}
|
|
case ESlateTransformSubComponent::Z:
|
|
{
|
|
ControlElement->Settings.LimitEnabled[8].SetForValueType(ValueType, Value);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
HierarchyBeingCustomized->SetControlSettings(ControlElement, ControlElement->Settings, true, true, true);
|
|
}
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
TransformWidgetArgs.AllowEditRotationRepresentation(true);
|
|
TransformWidgetArgs.DisplayRelativeWorld(true);
|
|
TransformWidgetArgs.DisplayToggle(false);
|
|
TransformWidgetArgs._OnGetToggleChecked.Unbind();
|
|
TransformWidgetArgs._OnToggleChanged.Unbind();
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto GetRelativeAbsoluteTransforms = [CurrentTransformType, Keys, HierarchyBeingDebugged](
|
|
const FRigElementKey& Key,
|
|
ERigTransformElementDetailsTransform::Type InTransformType = ERigTransformElementDetailsTransform::Max
|
|
) -> TPair<FEulerTransform, FEulerTransform>
|
|
{
|
|
if(InTransformType == ERigTransformElementDetailsTransform::Max)
|
|
{
|
|
InTransformType = CurrentTransformType;
|
|
}
|
|
|
|
FEulerTransform RelativeTransform = FEulerTransform::Identity;
|
|
FEulerTransform AbsoluteTransform = FEulerTransform::Identity;
|
|
|
|
const bool bInitial = InTransformType == ERigTransformElementDetailsTransform::Initial;
|
|
if(bInitial || InTransformType == ERigTransformElementDetailsTransform::Current)
|
|
{
|
|
RelativeTransform.FromFTransform(HierarchyBeingDebugged->GetLocalTransform(Key, bInitial));
|
|
AbsoluteTransform.FromFTransform(HierarchyBeingDebugged->GetGlobalTransform(Key, bInitial));
|
|
|
|
if(FRigControlElement* ControlElement = HierarchyBeingDebugged->Find<FRigControlElement>(Key))
|
|
{
|
|
switch(ControlElement->Settings.ControlType)
|
|
{
|
|
case ERigControlType::Rotator:
|
|
case ERigControlType::EulerTransform:
|
|
case ERigControlType::Transform:
|
|
case ERigControlType::TransformNoScale:
|
|
{
|
|
RelativeTransform.Rotation = HierarchyBeingDebugged->GetControlPreferredRotator(ControlElement, bInitial);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(FRigControlElement* ControlElement = HierarchyBeingDebugged->Find<FRigControlElement>(Key))
|
|
{
|
|
const ERigControlType ControlType = ControlElement->Settings.ControlType;
|
|
|
|
if(InTransformType == ERigTransformElementDetailsTransform::Offset)
|
|
{
|
|
RelativeTransform.FromFTransform(HierarchyBeingDebugged->GetControlOffsetTransform(ControlElement, ERigTransformType::InitialLocal));
|
|
AbsoluteTransform.FromFTransform(HierarchyBeingDebugged->GetControlOffsetTransform(ControlElement, ERigTransformType::InitialGlobal));
|
|
}
|
|
else if(InTransformType == ERigTransformElementDetailsTransform::Minimum)
|
|
{
|
|
switch(ControlType)
|
|
{
|
|
case ERigControlType::Position:
|
|
{
|
|
const FVector Data =
|
|
(FVector)HierarchyBeingDebugged->GetControlValue(ControlElement, ERigControlValueType::Minimum)
|
|
.Get<FVector3f>();
|
|
AbsoluteTransform = RelativeTransform = FEulerTransform(Data, FRotator::ZeroRotator, FVector::OneVector);
|
|
break;
|
|
}
|
|
case ERigControlType::Rotator:
|
|
{
|
|
const FVector Data =
|
|
(FVector)HierarchyBeingDebugged->GetControlValue(ControlElement, ERigControlValueType::Minimum)
|
|
.Get<FVector3f>();
|
|
FRotator Rotator = FRotator::MakeFromEuler(Data);
|
|
AbsoluteTransform = RelativeTransform = FEulerTransform(FVector::ZeroVector, Rotator, FVector::OneVector);
|
|
break;
|
|
}
|
|
case ERigControlType::Scale:
|
|
{
|
|
const FVector Data =
|
|
(FVector)HierarchyBeingDebugged->GetControlValue(ControlElement, ERigControlValueType::Minimum)
|
|
.Get<FVector3f>();
|
|
AbsoluteTransform = RelativeTransform = FEulerTransform(FVector::ZeroVector, FRotator::ZeroRotator, Data);
|
|
break;
|
|
}
|
|
case ERigControlType::EulerTransform:
|
|
{
|
|
const FRigControlValue::FEulerTransform_Float EulerTransform =
|
|
HierarchyBeingDebugged->GetControlValue(ControlElement, ERigControlValueType::Minimum)
|
|
.Get<FRigControlValue::FEulerTransform_Float>();
|
|
AbsoluteTransform = RelativeTransform = EulerTransform.ToTransform();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if(InTransformType == ERigTransformElementDetailsTransform::Maximum)
|
|
{
|
|
switch(ControlType)
|
|
{
|
|
case ERigControlType::Position:
|
|
{
|
|
const FVector Data =
|
|
(FVector)HierarchyBeingDebugged->GetControlValue(ControlElement, ERigControlValueType::Maximum)
|
|
.Get<FVector3f>();
|
|
AbsoluteTransform = RelativeTransform = FEulerTransform(Data, FRotator::ZeroRotator, FVector::OneVector);
|
|
break;
|
|
}
|
|
case ERigControlType::Rotator:
|
|
{
|
|
const FVector Data =
|
|
(FVector)HierarchyBeingDebugged->GetControlValue(ControlElement, ERigControlValueType::Maximum)
|
|
.Get<FVector3f>();
|
|
FRotator Rotator = FRotator::MakeFromEuler(Data);
|
|
AbsoluteTransform = RelativeTransform = FEulerTransform(FVector::ZeroVector, Rotator, FVector::OneVector);
|
|
break;
|
|
}
|
|
case ERigControlType::Scale:
|
|
{
|
|
const FVector Data =
|
|
(FVector)HierarchyBeingDebugged->GetControlValue(ControlElement, ERigControlValueType::Maximum)
|
|
.Get<FVector3f>();
|
|
AbsoluteTransform = RelativeTransform = FEulerTransform(FVector::ZeroVector, FRotator::ZeroRotator, Data);
|
|
break;
|
|
}
|
|
case ERigControlType::EulerTransform:
|
|
{
|
|
const FRigControlValue::FEulerTransform_Float EulerTransform =
|
|
HierarchyBeingDebugged->GetControlValue(ControlElement, ERigControlValueType::Maximum)
|
|
.Get<FRigControlValue::FEulerTransform_Float>();
|
|
AbsoluteTransform = RelativeTransform = EulerTransform.ToTransform();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TPair<FEulerTransform, FEulerTransform>(RelativeTransform, AbsoluteTransform);
|
|
};
|
|
|
|
|
|
auto GetCombinedTransform = [IsComponentRelative, GetRelativeAbsoluteTransforms](
|
|
const FRigElementKey& Key,
|
|
ERigTransformElementDetailsTransform::Type InTransformType = ERigTransformElementDetailsTransform::Max
|
|
) -> FEulerTransform
|
|
{
|
|
const TPair<FEulerTransform, FEulerTransform> TransformPair = GetRelativeAbsoluteTransforms(Key, InTransformType);
|
|
const FEulerTransform RelativeTransform = TransformPair.Key;
|
|
const FEulerTransform AbsoluteTransform = TransformPair.Value;
|
|
|
|
FEulerTransform Xfo;
|
|
Xfo.SetLocation((IsComponentRelative(0)) ? RelativeTransform.GetLocation() : AbsoluteTransform.GetLocation());
|
|
Xfo.SetRotator((IsComponentRelative(1)) ? RelativeTransform.Rotator() : AbsoluteTransform.Rotator());
|
|
Xfo.SetScale3D((IsComponentRelative(2)) ? RelativeTransform.GetScale3D() : AbsoluteTransform.GetScale3D());
|
|
|
|
return Xfo;
|
|
};
|
|
|
|
auto GetSingleTransform = [GetRelativeAbsoluteTransforms](
|
|
const FRigElementKey& Key,
|
|
bool bIsRelative,
|
|
ERigTransformElementDetailsTransform::Type InTransformType = ERigTransformElementDetailsTransform::Max
|
|
) -> FEulerTransform
|
|
{
|
|
const TPair<FEulerTransform, FEulerTransform> TransformPair = GetRelativeAbsoluteTransforms(Key, InTransformType);
|
|
const FEulerTransform RelativeTransform = TransformPair.Key;
|
|
const FEulerTransform AbsoluteTransform = TransformPair.Value;
|
|
return bIsRelative ? RelativeTransform : AbsoluteTransform;
|
|
};
|
|
|
|
auto SetSingleTransform = [CurrentTransformType, GetRelativeAbsoluteTransforms, this, HierarchyBeingDebugged](
|
|
const FRigElementKey& Key,
|
|
FEulerTransform InTransform,
|
|
bool bIsRelative,
|
|
bool bSetupUndoRedo)
|
|
{
|
|
const bool bCurrent = CurrentTransformType == ERigTransformElementDetailsTransform::Current;
|
|
const bool bInitial = CurrentTransformType == ERigTransformElementDetailsTransform::Initial;
|
|
|
|
bool bSetupModeEnabled = false;
|
|
if (UControlRig* DebuggedRig = Cast<UControlRig>(BlueprintBeingCustomized->GetObjectBeingDebugged()))
|
|
{
|
|
bSetupModeEnabled = DebuggedRig->IsSetupModeEnabled();
|
|
}
|
|
|
|
TArray<URigHierarchy*> HierarchiesToUpdate;
|
|
HierarchiesToUpdate.Add(HierarchyBeingDebugged);
|
|
if(!bCurrent || bSetupModeEnabled)
|
|
{
|
|
HierarchiesToUpdate.Add(HierarchyBeingCustomized);
|
|
}
|
|
|
|
for(URigHierarchy* HierarchyToUpdate : HierarchiesToUpdate)
|
|
{
|
|
if(bInitial || CurrentTransformType == ERigTransformElementDetailsTransform::Current)
|
|
{
|
|
if(bIsRelative)
|
|
{
|
|
HierarchyToUpdate->SetLocalTransform(Key, InTransform.ToFTransform(), bInitial, true, bSetupUndoRedo);
|
|
|
|
if(FRigControlElement* ControlElement = HierarchyToUpdate->Find<FRigControlElement>(Key))
|
|
{
|
|
switch(ControlElement->Settings.ControlType)
|
|
{
|
|
case ERigControlType::Rotator:
|
|
case ERigControlType::EulerTransform:
|
|
case ERigControlType::Transform:
|
|
case ERigControlType::TransformNoScale:
|
|
{
|
|
HierarchyToUpdate->SetControlPreferredRotator(ControlElement, InTransform.Rotator(), bInitial);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HierarchyToUpdate->SetGlobalTransform(Key, InTransform.ToFTransform(), bInitial, true, bSetupUndoRedo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(FRigControlElement* ControlElement = HierarchyToUpdate->Find<FRigControlElement>(Key))
|
|
{
|
|
const ERigControlType ControlType = ControlElement->Settings.ControlType;
|
|
|
|
if(CurrentTransformType == ERigTransformElementDetailsTransform::Offset)
|
|
{
|
|
if(!bIsRelative)
|
|
{
|
|
const FTransform ParentTransform = HierarchyToUpdate->GetParentTransform(Key, bInitial);
|
|
InTransform.FromFTransform(InTransform.ToFTransform().GetRelativeTransform(ParentTransform));
|
|
}
|
|
HierarchyToUpdate->SetControlOffsetTransform(Key, InTransform.ToFTransform(), true, true, bSetupUndoRedo);
|
|
}
|
|
else if(CurrentTransformType == ERigTransformElementDetailsTransform::Minimum)
|
|
{
|
|
switch(ControlType)
|
|
{
|
|
case ERigControlType::Position:
|
|
{
|
|
const FRigControlValue Value = FRigControlValue::Make<FVector3f>((FVector3f)InTransform.GetLocation());
|
|
HierarchyToUpdate->SetControlValue(ControlElement, Value, ERigControlValueType::Minimum, bSetupUndoRedo, true);
|
|
break;
|
|
}
|
|
case ERigControlType::Rotator:
|
|
{
|
|
const FVector3f Euler = (FVector3f)InTransform.Rotator().Euler();
|
|
const FRigControlValue Value = FRigControlValue::Make<FVector3f>(Euler);
|
|
HierarchyToUpdate->SetControlValue(ControlElement, Value, ERigControlValueType::Minimum, bSetupUndoRedo, true);
|
|
break;
|
|
}
|
|
case ERigControlType::Scale:
|
|
{
|
|
const FRigControlValue Value = FRigControlValue::Make<FVector3f>((FVector3f)InTransform.GetScale3D());
|
|
HierarchyToUpdate->SetControlValue(ControlElement, Value, ERigControlValueType::Minimum, bSetupUndoRedo, true);
|
|
break;
|
|
}
|
|
case ERigControlType::EulerTransform:
|
|
{
|
|
const FRigControlValue Value = FRigControlValue::Make<FRigControlValue::FEulerTransform_Float>(InTransform);
|
|
HierarchyToUpdate->SetControlValue(ControlElement, Value, ERigControlValueType::Minimum, bSetupUndoRedo, true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if(CurrentTransformType == ERigTransformElementDetailsTransform::Maximum)
|
|
{
|
|
switch(ControlType)
|
|
{
|
|
case ERigControlType::Position:
|
|
{
|
|
const FRigControlValue Value = FRigControlValue::Make<FVector3f>((FVector3f)InTransform.GetLocation());
|
|
HierarchyToUpdate->SetControlValue(ControlElement, Value, ERigControlValueType::Maximum, bSetupUndoRedo, true);
|
|
break;
|
|
}
|
|
case ERigControlType::Rotator:
|
|
{
|
|
const FVector3f Euler = (FVector3f)InTransform.Rotator().Euler();
|
|
const FRigControlValue Value = FRigControlValue::Make<FVector3f>(Euler);
|
|
HierarchyToUpdate->SetControlValue(ControlElement, Value, ERigControlValueType::Maximum, bSetupUndoRedo, true);
|
|
break;
|
|
}
|
|
case ERigControlType::Scale:
|
|
{
|
|
const FRigControlValue Value = FRigControlValue::Make<FVector3f>((FVector3f)InTransform.GetScale3D());
|
|
HierarchyToUpdate->SetControlValue(ControlElement, Value, ERigControlValueType::Maximum, bSetupUndoRedo, true);
|
|
break;
|
|
}
|
|
case ERigControlType::EulerTransform:
|
|
{
|
|
const FRigControlValue Value = FRigControlValue::Make<FRigControlValue::FEulerTransform_Float>(InTransform);
|
|
HierarchyToUpdate->SetControlValue(ControlElement, Value, ERigControlValueType::Maximum, bSetupUndoRedo, true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
TransformWidgetArgs.OnGetNumericValue_Lambda([Keys, GetCombinedTransform](
|
|
ESlateTransformComponent::Type Component,
|
|
ESlateRotationRepresentation::Type Representation,
|
|
ESlateTransformSubComponent::Type SubComponent) -> TOptional<FVector::FReal>
|
|
{
|
|
TOptional<FVector::FReal> FirstValue;
|
|
|
|
for(int32 Index = 0; Index < Keys.Num(); Index++)
|
|
{
|
|
const FRigElementKey& Key = Keys[Index];
|
|
FEulerTransform Xfo = GetCombinedTransform(Key);
|
|
|
|
TOptional<FVector::FReal> CurrentValue = SAdvancedTransformInputBox<FEulerTransform>::GetNumericValueFromTransform(Xfo, Component, Representation, SubComponent);
|
|
if(!CurrentValue.IsSet())
|
|
{
|
|
return CurrentValue;
|
|
}
|
|
|
|
if(Index == 0)
|
|
{
|
|
FirstValue = CurrentValue;
|
|
}
|
|
else
|
|
{
|
|
if(!FMath::IsNearlyEqual(FirstValue.GetValue(), CurrentValue.GetValue()))
|
|
{
|
|
return TOptional<FVector::FReal>();
|
|
}
|
|
}
|
|
}
|
|
|
|
return FirstValue;
|
|
});
|
|
|
|
TransformWidgetArgs.OnNumericValueChanged_Lambda(
|
|
[
|
|
Keys,
|
|
this,
|
|
IsComponentRelative,
|
|
GetSingleTransform,
|
|
SetSingleTransform,
|
|
CurrentTransformType
|
|
](
|
|
ESlateTransformComponent::Type Component,
|
|
ESlateRotationRepresentation::Type Representation,
|
|
ESlateTransformSubComponent::Type SubComponent,
|
|
FVector::FReal InNumericValue)
|
|
{
|
|
const bool bIsRelative = IsComponentRelative((int32)Component);
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
FEulerTransform Transform = GetSingleTransform(Key, bIsRelative);
|
|
FEulerTransform PreviousTransform = Transform;
|
|
SAdvancedTransformInputBox<FEulerTransform>::ApplyNumericValueChange(Transform, InNumericValue, Component, Representation, SubComponent);
|
|
|
|
if(!FRigControlElementDetails::Equals(Transform, PreviousTransform))
|
|
{
|
|
if(!SliderTransaction.IsValid())
|
|
{
|
|
SliderTransaction = MakeShareable(new FScopedTransaction(NSLOCTEXT("ControlRigElementDetails", "ChangeNumericValue", "Change Numeric Value")));
|
|
HierarchyBeingCustomized->Modify();
|
|
}
|
|
|
|
SetSingleTransform(Key, Transform, bIsRelative, false);
|
|
}
|
|
}
|
|
});
|
|
|
|
TransformWidgetArgs.OnNumericValueCommitted_Lambda(
|
|
[
|
|
Keys,
|
|
this,
|
|
IsComponentRelative,
|
|
GetSingleTransform,
|
|
SetSingleTransform,
|
|
CurrentTransformType
|
|
](
|
|
ESlateTransformComponent::Type Component,
|
|
ESlateRotationRepresentation::Type Representation,
|
|
ESlateTransformSubComponent::Type SubComponent,
|
|
FVector::FReal InNumericValue,
|
|
ETextCommit::Type InCommitType)
|
|
{
|
|
const bool bIsRelative = IsComponentRelative((int32)Component);
|
|
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("ChangeNumericValue", "Change Numeric Value"));
|
|
if(!SliderTransaction.IsValid())
|
|
{
|
|
HierarchyBeingCustomized->Modify();
|
|
}
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
FEulerTransform Transform = GetSingleTransform(Key, bIsRelative);
|
|
SAdvancedTransformInputBox<FEulerTransform>::ApplyNumericValueChange(Transform, InNumericValue, Component, Representation, SubComponent);
|
|
SetSingleTransform(Key, Transform, bIsRelative, true);
|
|
}
|
|
}
|
|
|
|
SliderTransaction.Reset();
|
|
});
|
|
|
|
TransformWidgetArgs.OnCopyToClipboard_Lambda([Keys, IsComponentRelative, ConformComponentRelative, GetSingleTransform](
|
|
ESlateTransformComponent::Type InComponent
|
|
)
|
|
{
|
|
if(Keys.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// make sure that we use the same relative setting on all components when copying
|
|
ConformComponentRelative(0);
|
|
const bool bIsRelative = IsComponentRelative(0);
|
|
|
|
const FRigElementKey& FirstKey = Keys[0];
|
|
FEulerTransform Xfo = GetSingleTransform(FirstKey, bIsRelative);
|
|
|
|
FString Content;
|
|
switch(InComponent)
|
|
{
|
|
case ESlateTransformComponent::Location:
|
|
{
|
|
const FVector Data = Xfo.GetLocation();
|
|
TBaseStructure<FVector>::Get()->ExportText(Content, &Data, &Data, nullptr, PPF_None, nullptr);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Rotation:
|
|
{
|
|
const FRotator Data = Xfo.Rotator();
|
|
TBaseStructure<FRotator>::Get()->ExportText(Content, &Data, &Data, nullptr, PPF_None, nullptr);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Scale:
|
|
{
|
|
const FVector Data = Xfo.GetScale3D();
|
|
TBaseStructure<FVector>::Get()->ExportText(Content, &Data, &Data, nullptr, PPF_None, nullptr);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Max:
|
|
default:
|
|
{
|
|
TBaseStructure<FEulerTransform>::Get()->ExportText(Content, &Xfo, &Xfo, nullptr, PPF_None, nullptr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!Content.IsEmpty())
|
|
{
|
|
FPlatformApplicationMisc::ClipboardCopy(*Content);
|
|
}
|
|
});
|
|
|
|
TransformWidgetArgs.OnPasteFromClipboard_Lambda([this, Keys, IsComponentRelative, ConformComponentRelative, GetSingleTransform, SetSingleTransform](
|
|
ESlateTransformComponent::Type InComponent
|
|
)
|
|
{
|
|
if(Keys.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
// make sure that we use the same relative setting on all components when pasting
|
|
ConformComponentRelative(0);
|
|
const bool bIsRelative = IsComponentRelative(0);
|
|
|
|
FString Content;
|
|
FPlatformApplicationMisc::ClipboardPaste(Content);
|
|
|
|
if(Content.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FScopedTransaction Transaction(LOCTEXT("PasteTransform", "Paste Transform"));
|
|
HierarchyBeingCustomized->Modify();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
FEulerTransform Xfo = GetSingleTransform(Key, bIsRelative);
|
|
{
|
|
class FRigPasteTransformWidgetErrorPipe : public FOutputDevice
|
|
{
|
|
public:
|
|
|
|
int32 NumErrors;
|
|
|
|
FRigPasteTransformWidgetErrorPipe()
|
|
: FOutputDevice()
|
|
, NumErrors(0)
|
|
{
|
|
}
|
|
|
|
virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category) override
|
|
{
|
|
UE_LOG(LogControlRig, Error, TEXT("Error Pasting to Widget: %s"), V);
|
|
NumErrors++;
|
|
}
|
|
};
|
|
|
|
FRigPasteTransformWidgetErrorPipe ErrorPipe;
|
|
|
|
switch(InComponent)
|
|
{
|
|
case ESlateTransformComponent::Location:
|
|
{
|
|
FVector Data = Xfo.GetLocation();
|
|
TBaseStructure<FVector>::Get()->ImportText(*Content, &Data, nullptr, PPF_None, &ErrorPipe, TBaseStructure<FVector>::Get()->GetName(), true);
|
|
Xfo.SetLocation(Data);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Rotation:
|
|
{
|
|
FRotator Data = Xfo.Rotator();
|
|
TBaseStructure<FRotator>::Get()->ImportText(*Content, &Data, nullptr, PPF_None, &ErrorPipe, TBaseStructure<FRotator>::Get()->GetName(), true);
|
|
Xfo.SetRotator(Data);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Scale:
|
|
{
|
|
FVector Data = Xfo.GetScale3D();
|
|
TBaseStructure<FVector>::Get()->ImportText(*Content, &Data, nullptr, PPF_None, &ErrorPipe, TBaseStructure<FVector>::Get()->GetName(), true);
|
|
Xfo.SetScale3D(Data);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Max:
|
|
default:
|
|
{
|
|
TBaseStructure<FEulerTransform>::Get()->ImportText(*Content, &Xfo, nullptr, PPF_None, &ErrorPipe, TBaseStructure<FEulerTransform>::Get()->GetName(), true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(ErrorPipe.NumErrors == 0)
|
|
{
|
|
SetSingleTransform(Key, Xfo, bIsRelative, true);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
TransformWidgetArgs.DiffersFromDefault_Lambda([
|
|
CurrentTransformType,
|
|
Keys,
|
|
GetSingleTransform
|
|
|
|
](
|
|
ESlateTransformComponent::Type InComponent) -> bool
|
|
{
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
const FEulerTransform CurrentTransform = GetSingleTransform(Key, true);
|
|
FEulerTransform DefaultTransform;
|
|
|
|
switch(CurrentTransformType)
|
|
{
|
|
case ERigTransformElementDetailsTransform::Current:
|
|
{
|
|
DefaultTransform = GetSingleTransform(Key, true, ERigTransformElementDetailsTransform::Initial);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
DefaultTransform = FEulerTransform::Identity;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch(InComponent)
|
|
{
|
|
case ESlateTransformComponent::Location:
|
|
{
|
|
if(!DefaultTransform.GetLocation().Equals(CurrentTransform.GetLocation()))
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Rotation:
|
|
{
|
|
if(!DefaultTransform.Rotator().Equals(CurrentTransform.Rotator()))
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Scale:
|
|
{
|
|
if(!DefaultTransform.GetScale3D().Equals(CurrentTransform.GetScale3D()))
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
default: // also no component whole transform
|
|
{
|
|
if(!DefaultTransform.GetLocation().Equals(CurrentTransform.GetLocation()) ||
|
|
!DefaultTransform.Rotator().Equals(CurrentTransform.Rotator()) ||
|
|
!DefaultTransform.GetScale3D().Equals(CurrentTransform.GetScale3D()))
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
|
|
TransformWidgetArgs.OnResetToDefault_Lambda([this, CurrentTransformType, Keys, GetSingleTransform, SetSingleTransform](
|
|
ESlateTransformComponent::Type InComponent)
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("ResetTransformToDefault", "Reset Transform to Default"));
|
|
HierarchyBeingCustomized->Modify();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
FEulerTransform CurrentTransform = GetSingleTransform(Key, true);
|
|
FEulerTransform DefaultTransform;
|
|
|
|
switch(CurrentTransformType)
|
|
{
|
|
case ERigTransformElementDetailsTransform::Current:
|
|
{
|
|
DefaultTransform = GetSingleTransform(Key, true, ERigTransformElementDetailsTransform::Initial);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
DefaultTransform = FEulerTransform::Identity;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch(InComponent)
|
|
{
|
|
case ESlateTransformComponent::Location:
|
|
{
|
|
CurrentTransform.SetLocation(DefaultTransform.GetLocation());
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Rotation:
|
|
{
|
|
CurrentTransform.SetRotator(DefaultTransform.Rotator());
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Scale:
|
|
{
|
|
CurrentTransform.SetScale3D(DefaultTransform.GetScale3D());
|
|
break;
|
|
}
|
|
default: // whole transform / max component
|
|
{
|
|
CurrentTransform = DefaultTransform;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SetSingleTransform(Key, CurrentTransform, true, true);
|
|
}
|
|
});
|
|
|
|
SAdvancedTransformInputBox<FEulerTransform>::ConstructGroupedTransformRows(
|
|
CategoryBuilder,
|
|
Label,
|
|
Tooltip,
|
|
TransformWidgetArgs);
|
|
}
|
|
|
|
void FRigBoneElementDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
FRigTransformElementDetails::CustomizeDetails(DetailBuilder);
|
|
CustomizeTransform(DetailBuilder);
|
|
}
|
|
|
|
void FRigControlElementDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
FRigTransformElementDetails::CustomizeDetails(DetailBuilder);
|
|
|
|
if (HierarchyBeingCustomized == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ControlElements.Reset();
|
|
ObjectPerControl.Reset();;
|
|
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigControlElement>())
|
|
{
|
|
ControlElements.Add(ObjectBeingCustomized->GetContent<FRigControlElement>());
|
|
ObjectPerControl.Add(ObjectBeingCustomized.Get());
|
|
}
|
|
}
|
|
}
|
|
|
|
CustomizeControl(DetailBuilder);
|
|
CustomizeValue(DetailBuilder);
|
|
CustomizeTransform(DetailBuilder);
|
|
CustomizeShape(DetailBuilder);
|
|
}
|
|
|
|
void FRigControlElementDetails::CustomizeValue(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
if(ControlElements.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// only show this section if all controls are the same type
|
|
const ERigControlType ControlType = ControlElements[0].Settings.ControlType;
|
|
for(const FRigControlElement& ControlElement : ControlElements)
|
|
{
|
|
if(ControlElement.Settings.ControlType != ControlType)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// transforms don't show their value here - instead they are shown in the transform section
|
|
if(ControlType == ERigControlType::EulerTransform ||
|
|
ControlType == ERigControlType::Transform ||
|
|
ControlType == ERigControlType::TransformNoScale)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TArray<FText> Labels = {
|
|
LOCTEXT("Initial", "Initial"),
|
|
LOCTEXT("Current", "Current")
|
|
};
|
|
TArray<FText> Tooltips = {
|
|
LOCTEXT("ValueInitialTooltip", "The initial animation value of the control"),
|
|
LOCTEXT("ValueCurrentTooltip", "The current animation value of the control")
|
|
};
|
|
TArray<ERigControlValueType> ValueTypes = {
|
|
ERigControlValueType::Initial,
|
|
ERigControlValueType::Current
|
|
};
|
|
|
|
// bool doesn't have limits,
|
|
// transform types already got filtered out earlier.
|
|
// integers with enums don't have limits either
|
|
if(ControlType != ERigControlType::Bool &&
|
|
(ControlType != ERigControlType::Integer || ControlElements[0].Settings.ControlEnum.IsNull()))
|
|
{
|
|
Labels.Append({
|
|
LOCTEXT("Min", "Min"),
|
|
LOCTEXT("Max", "Max")
|
|
});
|
|
Tooltips.Append({
|
|
LOCTEXT("ValueMinimumTooltip", "The minimum limit(s) for the control"),
|
|
LOCTEXT("ValueMaximumTooltip", "The maximum limit(s) for the control")
|
|
});
|
|
ValueTypes.Append({
|
|
ERigControlValueType::Minimum,
|
|
ERigControlValueType::Maximum
|
|
});
|
|
}
|
|
|
|
IDetailCategoryBuilder& ValueCategory = DetailBuilder.EditCategory(TEXT("Value"), LOCTEXT("Value", "Value"));
|
|
|
|
if(!PickedValueTypes.IsValid())
|
|
{
|
|
PickedValueTypes = MakeShareable(new TArray<ERigControlValueType>({ERigControlValueType::Current}));
|
|
}
|
|
|
|
TSharedPtr<SSegmentedControl<ERigControlValueType>> ValueTypeChoiceWidget =
|
|
SSegmentedControl<ERigControlValueType>::Create(
|
|
ValueTypes,
|
|
Labels,
|
|
Tooltips,
|
|
*PickedValueTypes.Get(),
|
|
true,
|
|
SSegmentedControl<ERigControlValueType>::FOnValuesChanged::CreateLambda(
|
|
[](TArray<ERigControlValueType> NewSelection)
|
|
{
|
|
(*FRigControlElementDetails::PickedValueTypes.Get()) = NewSelection;
|
|
}
|
|
)
|
|
);
|
|
|
|
AddChoiceWidgetRow(ValueCategory, FText::FromString(TEXT("ValueType")), ValueTypeChoiceWidget.ToSharedRef());
|
|
|
|
|
|
TSharedRef<UE::Math::TVector<float>> IsComponentRelative = MakeShareable(new UE::Math::TVector<float>(1.f, 1.f, 1.f));
|
|
|
|
for(int32 Index=0; Index < ValueTypes.Num(); Index++)
|
|
{
|
|
const ERigControlValueType ValueType = ValueTypes[Index];
|
|
|
|
const TAttribute<EVisibility> VisibilityAttribute =
|
|
TAttribute<EVisibility>::CreateLambda([ValueType, ValueTypeChoiceWidget]()-> EVisibility
|
|
{
|
|
return ValueTypeChoiceWidget->HasValue(ValueType) ? EVisibility::Visible : EVisibility::Collapsed;
|
|
});
|
|
|
|
switch(ControlType)
|
|
{
|
|
case ERigControlType::Bool:
|
|
{
|
|
CreateBoolValueWidgetRow(ValueCategory, Labels[Index], Tooltips[Index], ValueType, VisibilityAttribute);
|
|
break;
|
|
}
|
|
case ERigControlType::Float:
|
|
{
|
|
CreateFloatValueWidgetRow(ValueCategory, Labels[Index], Tooltips[Index], ValueType, VisibilityAttribute);
|
|
break;
|
|
}
|
|
case ERigControlType::Integer:
|
|
{
|
|
bool bIsEnum = false;
|
|
const TArray<FRigElementKey> Keys = GetElementKeys();
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
if(const FRigControlElement* ControlElement = HierarchyBeingCustomized->Find<FRigControlElement>(Key))
|
|
{
|
|
if(!ControlElement->Settings.ControlEnum.IsNull())
|
|
{
|
|
bIsEnum = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bIsEnum)
|
|
{
|
|
CreateEnumValueWidgetRow(ValueCategory, Labels[Index], Tooltips[Index], ValueType, VisibilityAttribute);
|
|
}
|
|
else
|
|
{
|
|
CreateIntegerValueWidgetRow(ValueCategory, Labels[Index], Tooltips[Index], ValueType, VisibilityAttribute);
|
|
}
|
|
break;
|
|
}
|
|
case ERigControlType::Vector2D:
|
|
{
|
|
CreateVector2DValueWidgetRow(ValueCategory, Labels[Index], Tooltips[Index], ValueType, VisibilityAttribute);
|
|
break;
|
|
}
|
|
case ERigControlType::Position:
|
|
case ERigControlType::Rotator:
|
|
case ERigControlType::Scale:
|
|
{
|
|
ERigTransformElementDetailsTransform::Type CurrentTransformType = ERigTransformElementDetailsTransform::Current;
|
|
switch(ValueType)
|
|
{
|
|
case ERigControlValueType::Initial:
|
|
{
|
|
CurrentTransformType = ERigTransformElementDetailsTransform::Initial;
|
|
break;
|
|
}
|
|
case ERigControlValueType::Minimum:
|
|
{
|
|
CurrentTransformType = ERigTransformElementDetailsTransform::Minimum;
|
|
break;
|
|
}
|
|
case ERigControlValueType::Maximum:
|
|
{
|
|
CurrentTransformType = ERigTransformElementDetailsTransform::Maximum;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SAdvancedTransformInputBox<FEulerTransform>::FArguments TransformWidgetArgs = SAdvancedTransformInputBox<FEulerTransform>::FArguments()
|
|
.DisplayToggle(false)
|
|
.DisplayRelativeWorld(true)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
.AllowEditRotationRepresentation(false)
|
|
.OnGetIsComponentRelative_Lambda(
|
|
[IsComponentRelative](ESlateTransformComponent::Type InComponent)
|
|
{
|
|
return IsComponentRelative->operator[]((int32)InComponent) > 0.f;
|
|
})
|
|
.OnIsComponentRelativeChanged_Lambda(
|
|
[IsComponentRelative](ESlateTransformComponent::Type InComponent, bool bIsRelative)
|
|
{
|
|
IsComponentRelative->operator[]((int32)InComponent) = bIsRelative ? 1.f : 0.f;
|
|
});
|
|
|
|
TransformWidgetArgs.Visibility(VisibilityAttribute);
|
|
|
|
TransformWidgetArgs.ConstructLocation(ControlType == ERigControlType::Position);
|
|
TransformWidgetArgs.ConstructRotation(ControlType == ERigControlType::Rotator);
|
|
TransformWidgetArgs.ConstructScale(ControlType == ERigControlType::Scale);
|
|
|
|
CreateEulerTransformValueWidgetRow(
|
|
GetHierarchyBeingDebugged(),
|
|
GetElementKeys(),
|
|
TransformWidgetArgs,
|
|
ValueCategory,
|
|
Labels[Index],
|
|
Tooltips[Index],
|
|
CurrentTransformType,
|
|
ValueType);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FRigControlElementDetails::CustomizeControl(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
const TSharedPtr<IPropertyHandle> SettingsHandle = DetailBuilder.GetProperty(TEXT("Settings"));
|
|
DetailBuilder.HideProperty(SettingsHandle);
|
|
|
|
IDetailCategoryBuilder& ControlCategory = DetailBuilder.EditCategory(TEXT("Control"), LOCTEXT("Control", "Control"));
|
|
|
|
const TSharedPtr<IPropertyHandle> DisplayNameHandle = SettingsHandle->GetChildHandle(TEXT("DisplayName"));
|
|
ControlCategory.AddCustomRow(LOCTEXT("DisplayName", "Display Name"))
|
|
.NameContent()
|
|
[
|
|
DisplayNameHandle->CreatePropertyNameWidget()
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
.Text(this, &FRigControlElementDetails::GetDisplayName)
|
|
.OnTextCommitted(this, &FRigControlElementDetails::SetDisplayName)
|
|
.IsEnabled(ObjectsBeingCustomized.Num() == 1)
|
|
];
|
|
|
|
const TSharedRef<IPropertyUtilities> PropertyUtilities = DetailBuilder.GetPropertyUtilities();
|
|
|
|
// when control type changes, we have to refresh detail panel
|
|
const TSharedPtr<IPropertyHandle> AnimationTypeHandle = SettingsHandle->GetChildHandle(TEXT("AnimationType"));
|
|
AnimationTypeHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda(
|
|
[this, PropertyUtilities]()
|
|
{
|
|
TArray<FRigControlElement> ControlElementsInView = GetElementsInDetailsView<FRigControlElement>();
|
|
TArray<FRigControlElement*> ControlElementsInHierarchy = GetElementsInHierarchy<FRigControlElement>();
|
|
check(ControlElementsInView.Num() == ControlElementsInHierarchy.Num());
|
|
|
|
if (this->HierarchyBeingCustomized && ControlElementsInHierarchy.Num() > 0)
|
|
{
|
|
this->HierarchyBeingCustomized->Modify();
|
|
|
|
for(int32 ControlIndex = 0; ControlIndex< ControlElementsInView.Num(); ControlIndex++)
|
|
{
|
|
const FRigControlElement& ViewElement = ControlElementsInView[ControlIndex];
|
|
FRigControlElement* ControlElement = ControlElementsInHierarchy[ControlIndex];
|
|
|
|
ControlElement->Settings.AnimationType = ViewElement.Settings.AnimationType;
|
|
|
|
ControlElement->Settings.bGroupWithParentControl =
|
|
ControlElement->Settings.ControlType == ERigControlType::Bool ||
|
|
ControlElement->Settings.ControlType == ERigControlType::Float ||
|
|
ControlElement->Settings.ControlType == ERigControlType::Integer ||
|
|
ControlElement->Settings.ControlType == ERigControlType::Vector2D;
|
|
|
|
switch(ControlElement->Settings.AnimationType)
|
|
{
|
|
case ERigControlAnimationType::AnimationControl:
|
|
{
|
|
ControlElement->Settings.ShapeVisibility = ERigControlVisibility::UserDefined;
|
|
ControlElement->Settings.bShapeVisible = true;
|
|
break;
|
|
}
|
|
case ERigControlAnimationType::AnimationChannel:
|
|
{
|
|
ControlElement->Settings.ShapeVisibility = ERigControlVisibility::UserDefined;
|
|
ControlElement->Settings.bShapeVisible = false;
|
|
break;
|
|
}
|
|
case ERigControlAnimationType::ProxyControl:
|
|
{
|
|
ControlElement->Settings.ShapeVisibility = ERigControlVisibility::BasedOnSelection;
|
|
ControlElement->Settings.bShapeVisible = true;
|
|
ControlElement->Settings.bGroupWithParentControl = false;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ControlElement->Settings.ShapeVisibility = ERigControlVisibility::UserDefined;
|
|
ControlElement->Settings.bShapeVisible = true;
|
|
ControlElement->Settings.bGroupWithParentControl = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
this->HierarchyBeingCustomized->SetControlSettings(ControlElement, ControlElement->Settings, true, true, true);
|
|
ObjectsBeingCustomized[ControlIndex]->SetContent<FRigControlElement>(*ControlElement);
|
|
|
|
if (this->HierarchyBeingCustomized != this->BlueprintBeingCustomized->Hierarchy)
|
|
{
|
|
if(FRigControlElement* OtherControlElement = this->BlueprintBeingCustomized->Hierarchy->Find<FRigControlElement>(ControlElement->GetKey()))
|
|
{
|
|
OtherControlElement->Settings = ControlElement->Settings;
|
|
this->BlueprintBeingCustomized->Hierarchy->SetControlSettings(OtherControlElement, OtherControlElement->Settings, true, true, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
PropertyUtilities->ForceRefresh();
|
|
}
|
|
}
|
|
));
|
|
|
|
ControlCategory.AddProperty(AnimationTypeHandle.ToSharedRef());
|
|
|
|
// when control type changes, we have to refresh detail panel
|
|
const TSharedPtr<IPropertyHandle> ControlTypeHandle = SettingsHandle->GetChildHandle(TEXT("ControlType"));
|
|
ControlTypeHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda(
|
|
[this, PropertyUtilities]()
|
|
{
|
|
TArray<FRigControlElement> ControlElementsInView = GetElementsInDetailsView<FRigControlElement>();
|
|
TArray<FRigControlElement*> ControlElementsInHierarchy = GetElementsInHierarchy<FRigControlElement>();
|
|
check(ControlElementsInView.Num() == ControlElementsInHierarchy.Num());
|
|
|
|
if (this->HierarchyBeingCustomized && ControlElementsInHierarchy.Num() > 0)
|
|
{
|
|
this->HierarchyBeingCustomized->Modify();
|
|
|
|
for(int32 ControlIndex = 0; ControlIndex< ControlElementsInView.Num(); ControlIndex++)
|
|
{
|
|
const FRigControlElement& ViewElement = ControlElementsInView[ControlIndex];
|
|
FRigControlElement* ControlElement = ControlElementsInHierarchy[ControlIndex];
|
|
|
|
FRigControlValue ValueToSet;
|
|
|
|
ControlElement->Settings.ControlType = ViewElement.Settings.ControlType;
|
|
ControlElement->Settings.LimitEnabled.Reset();
|
|
ControlElement->Settings.bGroupWithParentControl = false;
|
|
|
|
switch (ControlElement->Settings.ControlType)
|
|
{
|
|
case ERigControlType::Bool:
|
|
{
|
|
ControlElement->Settings.AnimationType = ERigControlAnimationType::AnimationChannel;
|
|
ValueToSet = FRigControlValue::Make<bool>(false);
|
|
ControlElement->Settings.bGroupWithParentControl = ControlElement->Settings.IsAnimatable();
|
|
break;
|
|
}
|
|
case ERigControlType::Float:
|
|
{
|
|
ValueToSet = FRigControlValue::Make<float>(0.f);
|
|
ControlElement->Settings.SetupLimitArrayForType(true);
|
|
ControlElement->Settings.MinimumValue = FRigControlValue::Make<float>(0.f);
|
|
ControlElement->Settings.MaximumValue = FRigControlValue::Make<float>(100.f);
|
|
ControlElement->Settings.bGroupWithParentControl = ControlElement->Settings.IsAnimatable();
|
|
break;
|
|
}
|
|
case ERigControlType::Integer:
|
|
{
|
|
ValueToSet = FRigControlValue::Make<int32>(0);
|
|
ControlElement->Settings.SetupLimitArrayForType(true);
|
|
ControlElement->Settings.MinimumValue = FRigControlValue::Make<int32>(0);
|
|
ControlElement->Settings.MaximumValue = FRigControlValue::Make<int32>(100);
|
|
ControlElement->Settings.bGroupWithParentControl = ControlElement->Settings.IsAnimatable();
|
|
break;
|
|
}
|
|
case ERigControlType::Vector2D:
|
|
{
|
|
ValueToSet = FRigControlValue::Make<FVector2D>(FVector2D::ZeroVector);
|
|
ControlElement->Settings.SetupLimitArrayForType(true);
|
|
ControlElement->Settings.MinimumValue = FRigControlValue::Make<FVector2D>(FVector2D::ZeroVector);
|
|
ControlElement->Settings.MaximumValue = FRigControlValue::Make<FVector2D>(FVector2D(100.f, 100.f));
|
|
ControlElement->Settings.bGroupWithParentControl = ControlElement->Settings.IsAnimatable();
|
|
break;
|
|
}
|
|
case ERigControlType::Position:
|
|
{
|
|
ValueToSet = FRigControlValue::Make<FVector>(FVector::ZeroVector);
|
|
ControlElement->Settings.SetupLimitArrayForType(false);
|
|
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.SetupLimitArrayForType(false);
|
|
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.SetupLimitArrayForType(false, false);
|
|
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.SetupLimitArrayForType(false, false, false);
|
|
ControlElement->Settings.MinimumValue = ValueToSet;
|
|
ControlElement->Settings.MaximumValue = ValueToSet;
|
|
break;
|
|
}
|
|
case ERigControlType::TransformNoScale:
|
|
{
|
|
FTransformNoScale Identity = FTransform::Identity;
|
|
ValueToSet = FRigControlValue::Make<FTransformNoScale>(Identity);
|
|
ControlElement->Settings.SetupLimitArrayForType(false, false, false);
|
|
ControlElement->Settings.MinimumValue = ValueToSet;
|
|
ControlElement->Settings.MaximumValue = ValueToSet;
|
|
break;
|
|
}
|
|
case ERigControlType::EulerTransform:
|
|
{
|
|
FEulerTransform Identity = FEulerTransform::Identity;
|
|
ValueToSet = FRigControlValue::Make<FEulerTransform>(Identity);
|
|
ControlElement->Settings.SetupLimitArrayForType(false, false, false);
|
|
ControlElement->Settings.MinimumValue = ValueToSet;
|
|
ControlElement->Settings.MaximumValue = ValueToSet;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ensure(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
this->HierarchyBeingCustomized->SetControlSettings(ControlElement, ControlElement->Settings, true, true, true);
|
|
this->HierarchyBeingCustomized->SetControlValue(ControlElement, ValueToSet, ERigControlValueType::Initial, true, false, true);
|
|
this->HierarchyBeingCustomized->SetControlValue(ControlElement, ValueToSet, ERigControlValueType::Current, true, false, true);
|
|
|
|
ObjectsBeingCustomized[ControlIndex]->SetContent<FRigControlElement>(*ControlElement);
|
|
|
|
if (this->HierarchyBeingCustomized != this->BlueprintBeingCustomized->Hierarchy)
|
|
{
|
|
if(FRigControlElement* OtherControlElement = this->BlueprintBeingCustomized->Hierarchy->Find<FRigControlElement>(ControlElement->GetKey()))
|
|
{
|
|
OtherControlElement->Settings = ControlElement->Settings;
|
|
this->BlueprintBeingCustomized->Hierarchy->SetControlSettings(OtherControlElement, OtherControlElement->Settings, true, true, true);
|
|
this->BlueprintBeingCustomized->Hierarchy->SetControlValue(OtherControlElement, ValueToSet, ERigControlValueType::Initial, true);
|
|
this->BlueprintBeingCustomized->Hierarchy->SetControlValue(OtherControlElement, ValueToSet, ERigControlValueType::Current, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
PropertyUtilities->ForceRefresh();
|
|
}
|
|
}
|
|
));
|
|
|
|
ControlCategory.AddProperty(ControlTypeHandle.ToSharedRef());
|
|
|
|
const bool bSupportsShape = !IsAnyControlOfAnimationType(ERigControlAnimationType::AnimationChannel) &&
|
|
!IsAnyControlOfAnimationType(ERigControlAnimationType::VisualCue);
|
|
|
|
if (HierarchyBeingCustomized != nullptr)
|
|
{
|
|
bool bEnableGroupWithParentControl = true;
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigControlElement>())
|
|
{
|
|
bool bSingleEnableGroupWithParentControl = false;
|
|
const FRigElementKey Key = ObjectBeingCustomized->GetContent<FRigControlElement>().GetKey();
|
|
if(const FRigControlElement* ControlElement = HierarchyBeingCustomized->Find<FRigControlElement>(Key))
|
|
{
|
|
if(const FRigControlElement* ParentElement =
|
|
Cast<FRigControlElement>(HierarchyBeingCustomized->GetFirstParent(ControlElement)))
|
|
{
|
|
if(ControlElement->Settings.IsAnimatable() &&
|
|
HierarchyBeingCustomized->GetChildren(ControlElement).IsEmpty())
|
|
{
|
|
bSingleEnableGroupWithParentControl = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!bSingleEnableGroupWithParentControl)
|
|
{
|
|
bEnableGroupWithParentControl = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(bEnableGroupWithParentControl)
|
|
{
|
|
const TSharedPtr<IPropertyHandle> GroupWithParentControlHandle = SettingsHandle->GetChildHandle(TEXT("bGroupWithParentControl"));
|
|
ControlCategory.AddProperty(GroupWithParentControlHandle.ToSharedRef()).DisplayName(FText::FromString(TEXT("Group Channels")));
|
|
}
|
|
}
|
|
|
|
if(bSupportsShape &&
|
|
!(IsAnyControlNotOfValueType(ERigControlType::Integer) &&
|
|
IsAnyControlNotOfValueType(ERigControlType::Float) &&
|
|
IsAnyControlNotOfValueType(ERigControlType::Vector2D)))
|
|
{
|
|
const TSharedPtr<IPropertyHandle> PrimaryAxisHandle = SettingsHandle->GetChildHandle(TEXT("PrimaryAxis"));
|
|
ControlCategory.AddProperty(PrimaryAxisHandle.ToSharedRef()).DisplayName(FText::FromString(TEXT("Primary Axis")));
|
|
}
|
|
|
|
if(IsAnyControlOfValueType(ERigControlType::Integer))
|
|
{
|
|
const TSharedPtr<IPropertyHandle> ControlEnumHandle = SettingsHandle->GetChildHandle(TEXT("ControlEnum"));
|
|
ControlCategory.AddProperty(ControlEnumHandle.ToSharedRef()).DisplayName(FText::FromString(TEXT("Control Enum")));
|
|
|
|
ControlEnumHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda(
|
|
[this, PropertyUtilities]()
|
|
{
|
|
PropertyUtilities->ForceRefresh();
|
|
|
|
if (this->HierarchyBeingCustomized != nullptr)
|
|
{
|
|
for(int32 ControlIndex = 0; ControlIndex < ControlElements.Num(); ControlIndex++)
|
|
{
|
|
const FRigControlElement ControlInView = ControlElements[ControlIndex];
|
|
FRigControlElement* ControlBeingCustomized = HierarchyBeingCustomized->Find<FRigControlElement>(ControlInView.GetKey());
|
|
|
|
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);
|
|
ControlBeingCustomized->Settings.LimitEnabled.Reset();
|
|
ControlBeingCustomized->Settings.LimitEnabled.Add(true);
|
|
HierarchyBeingCustomized->SetControlSettings(ControlBeingCustomized, ControlBeingCustomized->Settings, true, true, true);
|
|
|
|
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, false, false, true);
|
|
HierarchyBeingCustomized->SetControlValue(ControlBeingCustomized, CurrentValue, ERigControlValueType::Current, false, false, true);
|
|
|
|
if (UControlRig* DebuggedRig = Cast<UControlRig>(BlueprintBeingCustomized->GetObjectBeingDebugged()))
|
|
{
|
|
URigHierarchy* DebuggedHierarchy = DebuggedRig->GetHierarchy();
|
|
if(FRigControlElement* DebuggedControlElement = DebuggedHierarchy->Find<FRigControlElement>(ControlBeingCustomized->GetKey()))
|
|
{
|
|
DebuggedControlElement->Settings.MinimumValue.Set<int32>(0);
|
|
DebuggedControlElement->Settings.MaximumValue.Set<int32>(Maximum);
|
|
DebuggedHierarchy->SetControlSettings(DebuggedControlElement, DebuggedControlElement->Settings, true, true, true);
|
|
|
|
DebuggedHierarchy->SetControlValue(DebuggedControlElement, InitialValue, ERigControlValueType::Initial);
|
|
DebuggedHierarchy->SetControlValue(DebuggedControlElement, CurrentValue, ERigControlValueType::Current);
|
|
}
|
|
}
|
|
}
|
|
|
|
ObjectPerControl[ControlIndex]->SetContent<FRigControlElement>(*ControlBeingCustomized);
|
|
}
|
|
}
|
|
}
|
|
));
|
|
|
|
}
|
|
|
|
const TSharedPtr<IPropertyHandle> CustomizationHandle = SettingsHandle->GetChildHandle(TEXT("Customization"));
|
|
|
|
if(bSupportsShape)
|
|
{
|
|
const TSharedPtr<IPropertyHandle> AvailableSpacesHandle = CustomizationHandle->GetChildHandle(TEXT("AvailableSpaces"));
|
|
ControlCategory.AddProperty(AvailableSpacesHandle.ToSharedRef());
|
|
}
|
|
|
|
TArray<FRigElementKey> Keys = GetElementKeys();
|
|
URigHierarchy* HierarchyBeingDebugged = GetHierarchyBeingDebugged();
|
|
|
|
if(bSupportsShape)
|
|
{
|
|
const TSharedPtr<IPropertyHandle> DrawLimitsHandle = SettingsHandle->GetChildHandle(TEXT("bDrawLimits"));
|
|
|
|
ControlCategory
|
|
.AddProperty(DrawLimitsHandle.ToSharedRef()).DisplayName(FText::FromString(TEXT("Draw Limits")))
|
|
.IsEnabled(TAttribute<bool>::CreateLambda([Keys, HierarchyBeingDebugged]() -> bool
|
|
{
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
if(const FRigControlElement* ControlElement = HierarchyBeingDebugged->Find<FRigControlElement>(Key))
|
|
{
|
|
if(ControlElement->Settings.LimitEnabled.Contains(FRigControlLimitEnabled(true, true)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}));
|
|
}
|
|
|
|
if(!IsAnyControlNotOfAnimationType(ERigControlAnimationType::ProxyControl))
|
|
{
|
|
ControlCategory.AddProperty(SettingsHandle->GetChildHandle(TEXT("DrivenControls")).ToSharedRef());
|
|
}
|
|
}
|
|
|
|
void FRigControlElementDetails::CustomizeShape(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigControlElement>())
|
|
{
|
|
const FRigControlElement ControlElement = ObjectBeingCustomized->GetContent<FRigControlElement>();
|
|
if(!ControlElement.Settings.SupportsShape())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedPtr<IPropertyHandle> ShapeHandle = DetailBuilder.GetProperty(TEXT("Shape"));
|
|
TSharedPtr<IPropertyHandle> InitialHandle = ShapeHandle->GetChildHandle(TEXT("Initial"));
|
|
TSharedPtr<IPropertyHandle> LocalHandle = InitialHandle->GetChildHandle(TEXT("Local"));
|
|
ShapeTransformHandle = LocalHandle->GetChildHandle(TEXT("Transform"));
|
|
|
|
ShapeNameList.Reset();
|
|
if (BlueprintBeingCustomized)
|
|
{
|
|
const bool bUseNameSpace = BlueprintBeingCustomized->ShapeLibraries.Num() > 1;
|
|
for(TSoftObjectPtr<UControlRigShapeLibrary>& ShapeLibrary : BlueprintBeingCustomized->ShapeLibraries)
|
|
{
|
|
if (!ShapeLibrary.IsValid())
|
|
{
|
|
ShapeLibrary.LoadSynchronous();
|
|
}
|
|
if (ShapeLibrary.IsValid())
|
|
{
|
|
const FString NameSpace = bUseNameSpace ? ShapeLibrary->GetName() + TEXT(".") : FString();
|
|
ShapeNameList.Add(MakeShared<FString>(NameSpace + ShapeLibrary->DefaultShape.ShapeName.ToString()));
|
|
for (const FControlRigShapeDefinition& Shape : ShapeLibrary->Shapes)
|
|
{
|
|
ShapeNameList.Add(MakeShared<FString>(NameSpace + Shape.ShapeName.ToString()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
IDetailCategoryBuilder& ShapeCategory = DetailBuilder.EditCategory(TEXT("Shape"), LOCTEXT("Shape", "Shape"));
|
|
|
|
const TSharedPtr<IPropertyHandle> SettingsHandle = DetailBuilder.GetProperty(TEXT("Settings"));
|
|
|
|
if(!IsAnyControlNotOfAnimationType(ERigControlAnimationType::ProxyControl))
|
|
{
|
|
ShapeCategory.AddProperty(SettingsHandle->GetChildHandle(TEXT("ShapeVisibility")).ToSharedRef())
|
|
.DisplayName(FText::FromString(TEXT("Visibility Mode")));
|
|
}
|
|
|
|
ShapeCategory.AddProperty(SettingsHandle->GetChildHandle(TEXT("bShapeVisible")).ToSharedRef())
|
|
.DisplayName(FText::FromString(TEXT("Visible")));
|
|
|
|
IDetailGroup& ShapePropertiesGroup = ShapeCategory.AddGroup(TEXT("Shape Properties"), LOCTEXT("ShapeProperties", "Shape Properties"));
|
|
ShapePropertiesGroup.HeaderRow().NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
.Text(LOCTEXT("ShapeProperties", "Shape Properties"))
|
|
.ToolTipText(LOCTEXT("ShapePropertiesTooltip", "Customize the properties of the shape"))
|
|
]
|
|
.CopyAction(FUIAction(
|
|
FExecuteAction::CreateSP(this, &FRigControlElementDetails::OnCopyShapeProperties)))
|
|
.PasteAction(FUIAction(
|
|
FExecuteAction::CreateSP(this, &FRigControlElementDetails::OnPasteShapeProperties)));
|
|
|
|
// setup shape transform
|
|
SAdvancedTransformInputBox<FEulerTransform>::FArguments TransformWidgetArgs = SAdvancedTransformInputBox<FEulerTransform>::FArguments()
|
|
.DisplayToggle(false)
|
|
.DisplayRelativeWorld(false)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont());
|
|
|
|
TArray<FRigElementKey> Keys = GetElementKeys();
|
|
URigHierarchy* HierarchyBeingDebugged = GetHierarchyBeingDebugged();
|
|
Keys = HierarchyBeingDebugged->SortKeys(Keys);
|
|
|
|
auto GetShapeTransform = [HierarchyBeingDebugged](
|
|
const FRigElementKey& Key
|
|
) -> FEulerTransform
|
|
{
|
|
if(FRigControlElement* ControlElement = HierarchyBeingDebugged->Find<FRigControlElement>(Key))
|
|
{
|
|
return FEulerTransform(HierarchyBeingDebugged->GetControlShapeTransform(ControlElement, ERigTransformType::InitialLocal));
|
|
}
|
|
return FEulerTransform::Identity;
|
|
};
|
|
|
|
auto SetShapeTransform = [this](
|
|
const FRigElementKey& Key,
|
|
const FEulerTransform& InTransform,
|
|
bool bSetupUndo
|
|
)
|
|
{
|
|
if(FRigControlElement* ControlElement = HierarchyBeingCustomized->Find<FRigControlElement>(Key))
|
|
{
|
|
HierarchyBeingCustomized->SetControlShapeTransform(ControlElement, InTransform.ToFTransform(), ERigTransformType::InitialLocal, bSetupUndo, true, bSetupUndo);
|
|
}
|
|
};
|
|
|
|
TransformWidgetArgs.OnGetNumericValue_Lambda([Keys, HierarchyBeingDebugged, GetShapeTransform](
|
|
ESlateTransformComponent::Type Component,
|
|
ESlateRotationRepresentation::Type Representation,
|
|
ESlateTransformSubComponent::Type SubComponent) -> TOptional<FVector::FReal>
|
|
{
|
|
TOptional<FVector::FReal> FirstValue;
|
|
|
|
for(int32 Index = 0; Index < Keys.Num(); Index++)
|
|
{
|
|
const FRigElementKey& Key = Keys[Index];
|
|
FEulerTransform Xfo = GetShapeTransform(Key);
|
|
|
|
TOptional<FVector::FReal> CurrentValue = SAdvancedTransformInputBox<FEulerTransform>::GetNumericValueFromTransform(Xfo, Component, Representation, SubComponent);
|
|
if(!CurrentValue.IsSet())
|
|
{
|
|
return CurrentValue;
|
|
}
|
|
|
|
if(Index == 0)
|
|
{
|
|
FirstValue = CurrentValue;
|
|
}
|
|
else
|
|
{
|
|
if(!FMath::IsNearlyEqual(FirstValue.GetValue(), CurrentValue.GetValue()))
|
|
{
|
|
return TOptional<FVector::FReal>();
|
|
}
|
|
}
|
|
}
|
|
|
|
return FirstValue;
|
|
});
|
|
|
|
TransformWidgetArgs.OnNumericValueChanged_Lambda(
|
|
[
|
|
Keys,
|
|
this,
|
|
GetShapeTransform,
|
|
SetShapeTransform
|
|
](
|
|
ESlateTransformComponent::Type Component,
|
|
ESlateRotationRepresentation::Type Representation,
|
|
ESlateTransformSubComponent::Type SubComponent,
|
|
FVector::FReal InNumericValue)
|
|
{
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
FEulerTransform Transform = GetShapeTransform(Key);
|
|
FEulerTransform PreviousTransform = Transform;
|
|
SAdvancedTransformInputBox<FEulerTransform>::ApplyNumericValueChange(Transform, InNumericValue, Component, Representation, SubComponent);
|
|
|
|
if(!FRigControlElementDetails::Equals(Transform, PreviousTransform))
|
|
{
|
|
if(!SliderTransaction.IsValid())
|
|
{
|
|
SliderTransaction = MakeShareable(new FScopedTransaction(NSLOCTEXT("ControlRigElementDetails", "ChangeNumericValue", "Change Numeric Value")));
|
|
HierarchyBeingCustomized->Modify();
|
|
}
|
|
SetShapeTransform(Key, Transform, false);
|
|
}
|
|
}
|
|
});
|
|
|
|
TransformWidgetArgs.OnNumericValueCommitted_Lambda(
|
|
[
|
|
Keys,
|
|
this,
|
|
GetShapeTransform,
|
|
SetShapeTransform
|
|
](
|
|
ESlateTransformComponent::Type Component,
|
|
ESlateRotationRepresentation::Type Representation,
|
|
ESlateTransformSubComponent::Type SubComponent,
|
|
FVector::FReal InNumericValue,
|
|
ETextCommit::Type InCommitType)
|
|
{
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("ChangeNumericValue", "Change Numeric Value"));
|
|
this->HierarchyBeingCustomized->Modify();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
FEulerTransform Transform = GetShapeTransform(Key);
|
|
FEulerTransform PreviousTransform = Transform;
|
|
SAdvancedTransformInputBox<FEulerTransform>::ApplyNumericValueChange(Transform, InNumericValue, Component, Representation, SubComponent);
|
|
if(!FRigControlElementDetails::Equals(Transform, PreviousTransform))
|
|
{
|
|
SetShapeTransform(Key, Transform, true);
|
|
}
|
|
}
|
|
}
|
|
SliderTransaction.Reset();
|
|
});
|
|
|
|
TransformWidgetArgs.OnCopyToClipboard_Lambda([Keys, GetShapeTransform](
|
|
ESlateTransformComponent::Type InComponent
|
|
)
|
|
{
|
|
if(Keys.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FRigElementKey& FirstKey = Keys[0];
|
|
FEulerTransform Xfo = GetShapeTransform(FirstKey);
|
|
|
|
FString Content;
|
|
switch(InComponent)
|
|
{
|
|
case ESlateTransformComponent::Location:
|
|
{
|
|
const FVector Data = Xfo.GetLocation();
|
|
TBaseStructure<FVector>::Get()->ExportText(Content, &Data, &Data, nullptr, PPF_None, nullptr);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Rotation:
|
|
{
|
|
const FRotator Data = Xfo.Rotator();
|
|
TBaseStructure<FRotator>::Get()->ExportText(Content, &Data, &Data, nullptr, PPF_None, nullptr);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Scale:
|
|
{
|
|
const FVector Data = Xfo.GetScale3D();
|
|
TBaseStructure<FVector>::Get()->ExportText(Content, &Data, &Data, nullptr, PPF_None, nullptr);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Max:
|
|
default:
|
|
{
|
|
TBaseStructure<FEulerTransform>::Get()->ExportText(Content, &Xfo, &Xfo, nullptr, PPF_None, nullptr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!Content.IsEmpty())
|
|
{
|
|
FPlatformApplicationMisc::ClipboardCopy(*Content);
|
|
}
|
|
});
|
|
|
|
TransformWidgetArgs.OnPasteFromClipboard_Lambda([Keys, GetShapeTransform, SetShapeTransform, this](
|
|
ESlateTransformComponent::Type InComponent
|
|
)
|
|
{
|
|
if(Keys.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FString Content;
|
|
FPlatformApplicationMisc::ClipboardPaste(Content);
|
|
|
|
if(Content.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FScopedTransaction Transaction(LOCTEXT("PasteTransform", "Paste Transform"));
|
|
HierarchyBeingCustomized->Modify();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
FEulerTransform Xfo = GetShapeTransform(Key);
|
|
{
|
|
class FRigPasteTransformWidgetErrorPipe : public FOutputDevice
|
|
{
|
|
public:
|
|
|
|
int32 NumErrors;
|
|
|
|
FRigPasteTransformWidgetErrorPipe()
|
|
: FOutputDevice()
|
|
, NumErrors(0)
|
|
{
|
|
}
|
|
|
|
virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category) override
|
|
{
|
|
UE_LOG(LogControlRig, Error, TEXT("Error Pasting to Widget: %s"), V);
|
|
NumErrors++;
|
|
}
|
|
};
|
|
|
|
FRigPasteTransformWidgetErrorPipe ErrorPipe;
|
|
|
|
switch(InComponent)
|
|
{
|
|
case ESlateTransformComponent::Location:
|
|
{
|
|
FVector Data = Xfo.GetLocation();
|
|
TBaseStructure<FVector>::Get()->ImportText(*Content, &Data, nullptr, PPF_None, &ErrorPipe, TBaseStructure<FVector>::Get()->GetName(), true);
|
|
Xfo.SetLocation(Data);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Rotation:
|
|
{
|
|
FRotator Data = Xfo.Rotator();
|
|
TBaseStructure<FRotator>::Get()->ImportText(*Content, &Data, nullptr, PPF_None, &ErrorPipe, TBaseStructure<FRotator>::Get()->GetName(), true);
|
|
Xfo.SetRotator(Data);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Scale:
|
|
{
|
|
FVector Data = Xfo.GetScale3D();
|
|
TBaseStructure<FVector>::Get()->ImportText(*Content, &Data, nullptr, PPF_None, &ErrorPipe, TBaseStructure<FVector>::Get()->GetName(), true);
|
|
Xfo.SetScale3D(Data);
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Max:
|
|
default:
|
|
{
|
|
TBaseStructure<FEulerTransform>::Get()->ImportText(*Content, &Xfo, nullptr, PPF_None, &ErrorPipe, TBaseStructure<FEulerTransform>::Get()->GetName(), true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(ErrorPipe.NumErrors == 0)
|
|
{
|
|
SetShapeTransform(Key, Xfo, true);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
TransformWidgetArgs.DiffersFromDefault_Lambda([
|
|
Keys,
|
|
GetShapeTransform
|
|
](
|
|
ESlateTransformComponent::Type InComponent) -> bool
|
|
{
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
const FEulerTransform CurrentTransform = GetShapeTransform(Key);
|
|
static const FEulerTransform DefaultTransform = FEulerTransform::Identity;
|
|
|
|
switch(InComponent)
|
|
{
|
|
case ESlateTransformComponent::Location:
|
|
{
|
|
if(!DefaultTransform.GetLocation().Equals(CurrentTransform.GetLocation()))
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Rotation:
|
|
{
|
|
if(!DefaultTransform.Rotator().Equals(CurrentTransform.Rotator()))
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Scale:
|
|
{
|
|
if(!DefaultTransform.GetScale3D().Equals(CurrentTransform.GetScale3D()))
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
default: // also no component whole transform
|
|
{
|
|
if(!DefaultTransform.GetLocation().Equals(CurrentTransform.GetLocation()) ||
|
|
!DefaultTransform.Rotator().Equals(CurrentTransform.Rotator()) ||
|
|
!DefaultTransform.GetScale3D().Equals(CurrentTransform.GetScale3D()))
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
|
|
TransformWidgetArgs.OnResetToDefault_Lambda([Keys, GetShapeTransform, SetShapeTransform, this](
|
|
ESlateTransformComponent::Type InComponent)
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("ResetTransformToDefault", "Reset Transform to Default"));
|
|
HierarchyBeingCustomized->Modify();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
FEulerTransform CurrentTransform = GetShapeTransform(Key);
|
|
static const FEulerTransform DefaultTransform = FEulerTransform::Identity;
|
|
|
|
switch(InComponent)
|
|
{
|
|
case ESlateTransformComponent::Location:
|
|
{
|
|
CurrentTransform.SetLocation(DefaultTransform.GetLocation());
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Rotation:
|
|
{
|
|
CurrentTransform.SetRotator(DefaultTransform.Rotator());
|
|
break;
|
|
}
|
|
case ESlateTransformComponent::Scale:
|
|
{
|
|
CurrentTransform.SetScale3D(DefaultTransform.GetScale3D());
|
|
break;
|
|
}
|
|
default: // whole transform / max component
|
|
{
|
|
CurrentTransform = DefaultTransform;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SetShapeTransform(Key, CurrentTransform, true);
|
|
}
|
|
});
|
|
|
|
SAdvancedTransformInputBox<FEulerTransform>::ConstructGroupedTransformRows(
|
|
ShapeCategory,
|
|
LOCTEXT("ShapeTransform", "Shape Transform"),
|
|
LOCTEXT("ShapeTransformTooltip", "The relative transform of the shape under the control"),
|
|
TransformWidgetArgs);
|
|
|
|
ShapeNameHandle = SettingsHandle->GetChildHandle(TEXT("ShapeName"));
|
|
ShapePropertiesGroup.AddPropertyRow(ShapeNameHandle.ToSharedRef()).CustomWidget()
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FText::FromString(TEXT("Shape")))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
.IsEnabled(this, &FRigControlElementDetails::IsShapeEnabled)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SAssignNew(ShapeNameListWidget, SControlRigShapeNameList, ControlElements, BlueprintBeingCustomized)
|
|
.OnGetNameListContent(this, &FRigControlElementDetails::GetShapeNameList)
|
|
.IsEnabled(this, &FRigControlElementDetails::IsShapeEnabled)
|
|
];
|
|
|
|
ShapeColorHandle = SettingsHandle->GetChildHandle(TEXT("ShapeColor"));
|
|
ShapePropertiesGroup.AddPropertyRow(ShapeColorHandle.ToSharedRef())
|
|
.DisplayName(FText::FromString(TEXT("Color")));
|
|
}
|
|
|
|
void FRigControlElementDetails::BeginDestroy()
|
|
{
|
|
FRigTransformElementDetails::BeginDestroy();
|
|
|
|
if(ShapeNameListWidget.IsValid())
|
|
{
|
|
ShapeNameListWidget->BeginDestroy();
|
|
}
|
|
}
|
|
|
|
void FRigControlElementDetails::RegisterSectionMappings(FPropertyEditorModule& PropertyEditorModule, UClass* InClass)
|
|
{
|
|
FRigTransformElementDetails::RegisterSectionMappings(PropertyEditorModule, InClass);
|
|
|
|
TSharedRef<FPropertySection> ControlSection = PropertyEditorModule.FindOrCreateSection(InClass->GetFName(), "Control", LOCTEXT("Control", "Control"));
|
|
ControlSection->AddCategory("General");
|
|
ControlSection->AddCategory("Control");
|
|
ControlSection->AddCategory("Value");
|
|
|
|
TSharedRef<FPropertySection> ShapeSection = PropertyEditorModule.FindOrCreateSection(InClass->GetFName(), "Shape", LOCTEXT("Shape", "Shape"));
|
|
ShapeSection->AddCategory("General");
|
|
ShapeSection->AddCategory("Shape");
|
|
}
|
|
|
|
bool FRigControlElementDetails::IsShapeEnabled() const
|
|
{
|
|
for(TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigControlElement>())
|
|
{
|
|
const FRigControlElement ControlElement = ObjectBeingCustomized->GetContent<FRigControlElement>();
|
|
return ControlElement.Settings.SupportsShape();
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const TArray<TSharedPtr<FString>>& FRigControlElementDetails::GetShapeNameList() const
|
|
{
|
|
return ShapeNameList;
|
|
}
|
|
|
|
FText FRigControlElementDetails::GetDisplayName() const
|
|
{
|
|
FName DisplayName(NAME_None);
|
|
|
|
for(int32 ObjectIndex = 0; ObjectIndex < ObjectsBeingCustomized.Num(); ObjectIndex++)
|
|
{
|
|
TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized = ObjectsBeingCustomized[ObjectIndex];
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigControlElement>())
|
|
{
|
|
const FRigControlElement ControlElement = ObjectBeingCustomized->GetContent<FRigControlElement>();
|
|
if(ObjectIndex == 0)
|
|
{
|
|
DisplayName = ControlElement.Settings.DisplayName;
|
|
}
|
|
else if(DisplayName != ControlElement.Settings.DisplayName)
|
|
{
|
|
return ControlRigDetailsMultipleValues;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!DisplayName.IsNone())
|
|
{
|
|
return FText::FromName(DisplayName);
|
|
}
|
|
return FText();
|
|
}
|
|
|
|
void FRigControlElementDetails::SetDisplayName(const FText& InNewText, ETextCommit::Type InCommitType)
|
|
{
|
|
if(InCommitType == ETextCommit::OnCleared)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FName DisplayName = InNewText.IsEmpty() ? FName(NAME_None) : FName(*InNewText.ToString());
|
|
|
|
for(int32 ObjectIndex = 0; ObjectIndex < ObjectsBeingCustomized.Num(); ObjectIndex++)
|
|
{
|
|
TWeakObjectPtr<UDetailsViewWrapperObject> ObjectBeingCustomized = ObjectsBeingCustomized[ObjectIndex];
|
|
if(ObjectBeingCustomized.IsValid())
|
|
{
|
|
if(ObjectBeingCustomized->IsChildOf<FRigControlElement>())
|
|
{
|
|
FRigControlElement ControlElement = ObjectBeingCustomized->GetContent<FRigControlElement>();
|
|
ControlElement.Settings.DisplayName = DisplayName;
|
|
ObjectBeingCustomized->SetContent<FRigControlElement>(ControlElement);
|
|
|
|
if(HierarchyBeingCustomized)
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("SetDisplayName", "SetDisplayName"));
|
|
HierarchyBeingCustomized->SetControlSettings(ControlElement.GetKey(), ControlElement.Settings, true, true, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FRigControlElementDetails::OnCopyShapeProperties()
|
|
{
|
|
FString Value;
|
|
if (ObjectsBeingCustomized.Num() > 0)
|
|
{
|
|
if(ObjectsBeingCustomized[0].IsValid())
|
|
{
|
|
if(ObjectsBeingCustomized[0]->IsChildOf<FRigControlElement>())
|
|
{
|
|
const FRigControlElement ControlElement = ObjectsBeingCustomized[0]->GetContent<FRigControlElement>();
|
|
|
|
Value = FString::Printf(TEXT("(ShapeName=\"%s\",ShapeColor=%s,Transform=%s)"),
|
|
*ControlElement.Settings.ShapeName.ToString(),
|
|
*ControlElement.Settings.ShapeColor.ToString(),
|
|
*ControlElement.Shape.Initial.Local.Transform.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Value.IsEmpty())
|
|
{
|
|
// Copy.
|
|
FPlatformApplicationMisc::ClipboardCopy(*Value);
|
|
}
|
|
}
|
|
|
|
void FRigControlElementDetails::OnPasteShapeProperties()
|
|
{
|
|
FString PastedText;
|
|
FPlatformApplicationMisc::ClipboardPaste(PastedText);
|
|
|
|
FString TrimmedText = PastedText.LeftChop(1).RightChop(1);
|
|
FString ShapeName;
|
|
FString ShapeColorStr;
|
|
FString TransformStr;
|
|
bool bSuccessful = FParse::Value(*TrimmedText, TEXT("ShapeName="), ShapeName) &&
|
|
FParse::Value(*TrimmedText, TEXT("ShapeColor="), ShapeColorStr, false) &&
|
|
FParse::Value(*TrimmedText, TEXT("Transform="), TransformStr, false);
|
|
|
|
if (bSuccessful)
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("PasteShape", "Paste Shape"));
|
|
|
|
// Name
|
|
{
|
|
ShapeNameHandle->NotifyPreChange();
|
|
ShapeNameHandle->SetValue(ShapeName);
|
|
ShapeNameHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
|
|
}
|
|
|
|
// Color
|
|
{
|
|
ShapeColorHandle->NotifyPreChange();
|
|
TArray<void*> RawDataPtrs;
|
|
ShapeColorHandle->AccessRawData(RawDataPtrs);
|
|
for (void* RawPtr: RawDataPtrs)
|
|
{
|
|
bSuccessful &= static_cast<FLinearColor*>(RawPtr)->InitFromString(ShapeColorStr);
|
|
if (!bSuccessful)
|
|
{
|
|
Transaction.Cancel();
|
|
return;
|
|
}
|
|
}
|
|
ShapeColorHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
|
|
}
|
|
|
|
// Transform
|
|
{
|
|
ShapeTransformHandle->NotifyPreChange();
|
|
TArray<void*> RawDataPtrs;
|
|
ShapeTransformHandle->AccessRawData(RawDataPtrs);
|
|
for (void* RawPtr: RawDataPtrs)
|
|
{
|
|
bSuccessful &= static_cast<FTransform*>(RawPtr)->InitFromString(TransformStr);
|
|
if (!bSuccessful)
|
|
{
|
|
Transaction.Cancel();
|
|
return;
|
|
}
|
|
}
|
|
ShapeTransformHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FRigControlElementDetails::CreateBoolValueWidgetRow(
|
|
IDetailCategoryBuilder& CategoryBuilder,
|
|
const FText& Label,
|
|
const FText& Tooltip,
|
|
ERigControlValueType ValueType,
|
|
TAttribute<EVisibility> Visibility)
|
|
{
|
|
const bool bCurrent = ValueType == ERigControlValueType::Current;
|
|
const bool bInitial = ValueType == ERigControlValueType::Initial;
|
|
|
|
TArray<FRigElementKey> Keys = GetElementKeys();
|
|
URigHierarchy* HierarchyBeingDebugged = GetHierarchyBeingDebugged();
|
|
URigHierarchy* HierarchyToChange = bCurrent ? HierarchyBeingDebugged : HierarchyBeingCustomized;
|
|
Keys = HierarchyBeingDebugged->SortKeys(Keys);
|
|
|
|
const static TCHAR* TrueText = TEXT("True");
|
|
const static TCHAR* FalseText = TEXT("False");
|
|
|
|
CategoryBuilder.AddCustomRow(Label)
|
|
.Visibility(Visibility)
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(Label)
|
|
.ToolTipText(Tooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked_Lambda([ValueType, Keys, HierarchyBeingDebugged]() -> ECheckBoxState
|
|
{
|
|
const bool FirstValue = HierarchyBeingDebugged->GetControlValue<bool>(Keys[0], ValueType);
|
|
for(int32 Index = 1; Index < Keys.Num(); Index++)
|
|
{
|
|
const bool SecondValue = HierarchyBeingDebugged->GetControlValue<bool>(Keys[Index], ValueType);
|
|
if(FirstValue != SecondValue)
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
}
|
|
return FirstValue ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
})
|
|
.OnCheckStateChanged_Lambda([ValueType, Keys, HierarchyToChange](ECheckBoxState NewState)
|
|
{
|
|
if(NewState == ECheckBoxState::Undetermined)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const bool Value = NewState == ECheckBoxState::Checked;
|
|
FScopedTransaction Transaction(LOCTEXT("ChangeValue", "Change Value"));
|
|
HierarchyToChange->Modify();
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<bool>(Value), ValueType, true, true);
|
|
}
|
|
})
|
|
]
|
|
.CopyAction(FUIAction(
|
|
FExecuteAction::CreateLambda([ValueType, Keys, HierarchyBeingDebugged]()
|
|
{
|
|
const bool FirstValue = HierarchyBeingDebugged->GetControlValue<bool>(Keys[0], ValueType);
|
|
FPlatformApplicationMisc::ClipboardCopy(FirstValue ? TrueText : FalseText);
|
|
}),
|
|
FCanExecuteAction())
|
|
)
|
|
.PasteAction(FUIAction(
|
|
FExecuteAction::CreateLambda([ValueType, Keys, HierarchyToChange]()
|
|
{
|
|
FString Content;
|
|
FPlatformApplicationMisc::ClipboardPaste(Content);
|
|
|
|
const bool Value = FToBoolHelper::FromCStringWide(*Content);
|
|
FScopedTransaction Transaction(LOCTEXT("ChangeValue", "Change Value"));
|
|
HierarchyToChange->Modify();
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<bool>(Value), ValueType, true, true);
|
|
}
|
|
}),
|
|
FCanExecuteAction())
|
|
)
|
|
.OverrideResetToDefault(FResetToDefaultOverride::Create(
|
|
TAttribute<bool>::CreateLambda([ValueType, Keys, HierarchyBeingDebugged]() -> bool
|
|
{
|
|
const bool FirstValue = HierarchyBeingDebugged->GetControlValue<bool>(Keys[0], ValueType);
|
|
const bool ReferenceValue = ValueType == ERigControlValueType::Initial ? false :
|
|
HierarchyBeingDebugged->GetControlValue<bool>(Keys[0], ERigControlValueType::Initial);
|
|
|
|
return FirstValue != ReferenceValue;
|
|
}),
|
|
FSimpleDelegate::CreateLambda([ValueType, Keys, HierarchyToChange]()
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("ResetValueToDefault", "Reset Value To Default"));
|
|
HierarchyToChange->Modify();
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
const bool ReferenceValue = ValueType == ERigControlValueType::Initial ? false :
|
|
HierarchyToChange->GetControlValue<bool>(Keys[0], ERigControlValueType::Initial);
|
|
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<bool>(ReferenceValue), ValueType, true, true);
|
|
}
|
|
})
|
|
));
|
|
}
|
|
|
|
void FRigControlElementDetails::CreateFloatValueWidgetRow(
|
|
IDetailCategoryBuilder& CategoryBuilder,
|
|
const FText& Label,
|
|
const FText& Tooltip,
|
|
ERigControlValueType ValueType,
|
|
TAttribute<EVisibility> Visibility)
|
|
{
|
|
CreateNumericValueWidgetRow<float>(CategoryBuilder, Label, Tooltip, ValueType, Visibility);
|
|
}
|
|
|
|
void FRigControlElementDetails::CreateIntegerValueWidgetRow(
|
|
IDetailCategoryBuilder& CategoryBuilder,
|
|
const FText& Label,
|
|
const FText& Tooltip,
|
|
ERigControlValueType ValueType,
|
|
TAttribute<EVisibility> Visibility)
|
|
{
|
|
CreateNumericValueWidgetRow<int32>(CategoryBuilder, Label, Tooltip, ValueType, Visibility);
|
|
}
|
|
|
|
void FRigControlElementDetails::CreateEnumValueWidgetRow(IDetailCategoryBuilder& CategoryBuilder, const FText& Label,
|
|
const FText& Tooltip, ERigControlValueType ValueType, TAttribute<EVisibility> Visibility)
|
|
{
|
|
const bool bCurrent = ValueType == ERigControlValueType::Current;
|
|
const bool bInitial = ValueType == ERigControlValueType::Initial;
|
|
|
|
TArray<FRigElementKey> Keys = GetElementKeys();
|
|
URigHierarchy* HierarchyBeingDebugged = GetHierarchyBeingDebugged();
|
|
URigHierarchy* HierarchyToChange = bCurrent ? HierarchyBeingDebugged : HierarchyBeingCustomized;
|
|
Keys = HierarchyBeingDebugged->SortKeys(Keys);
|
|
|
|
UEnum* Enum = nullptr;
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
if(const FRigControlElement* ControlElement = HierarchyBeingCustomized->Find<FRigControlElement>(Key))
|
|
{
|
|
Enum = ControlElement->Settings.ControlEnum.Get();
|
|
if(Enum)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
check(Enum != nullptr);
|
|
|
|
CategoryBuilder.AddCustomRow(Label)
|
|
.Visibility(Visibility)
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(Label)
|
|
.ToolTipText(Tooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SEnumComboBox, Enum)
|
|
.CurrentValue_Lambda([ValueType, Keys, HierarchyBeingDebugged]() -> int32
|
|
{
|
|
const int32 FirstValue = HierarchyBeingDebugged->GetControlValue<int32>(Keys[0], ValueType);
|
|
for(int32 Index = 1; Index < Keys.Num(); Index++)
|
|
{
|
|
const int32 SecondValue = HierarchyBeingDebugged->GetControlValue<int32>(Keys[Index], ValueType);
|
|
if(FirstValue != SecondValue)
|
|
{
|
|
return INDEX_NONE;
|
|
}
|
|
}
|
|
return FirstValue;
|
|
})
|
|
.OnEnumSelectionChanged_Lambda([ValueType, Keys, HierarchyToChange](int32 NewSelection, ESelectInfo::Type)
|
|
{
|
|
if(NewSelection == INDEX_NONE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FScopedTransaction Transaction(LOCTEXT("ChangeValue", "Change Value"));
|
|
HierarchyToChange->Modify();
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<int32>(NewSelection), ValueType, true, true);
|
|
}
|
|
})
|
|
.Font(FAppStyle::GetFontStyle(TEXT("MenuItem.Font")))
|
|
]
|
|
.CopyAction(FUIAction(
|
|
FExecuteAction::CreateLambda([ValueType, Keys, HierarchyBeingDebugged]()
|
|
{
|
|
const int32 FirstValue = HierarchyBeingDebugged->GetControlValue<int32>(Keys[0], ValueType);
|
|
FPlatformApplicationMisc::ClipboardCopy(*FString::FromInt(FirstValue));
|
|
}),
|
|
FCanExecuteAction())
|
|
)
|
|
.PasteAction(FUIAction(
|
|
FExecuteAction::CreateLambda([ValueType, Keys, HierarchyToChange]()
|
|
{
|
|
FString Content;
|
|
FPlatformApplicationMisc::ClipboardPaste(Content);
|
|
if(!Content.IsNumeric())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int32 Value = FCString::Atoi(*Content);
|
|
FScopedTransaction Transaction(LOCTEXT("ChangeValue", "Change Value"));
|
|
HierarchyToChange->Modify();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<int32>(Value), ValueType, true, true);
|
|
}
|
|
}),
|
|
FCanExecuteAction())
|
|
)
|
|
.OverrideResetToDefault(FResetToDefaultOverride::Create(
|
|
TAttribute<bool>::CreateLambda([ValueType, Keys, HierarchyBeingDebugged]() -> bool
|
|
{
|
|
const int32 FirstValue = HierarchyBeingDebugged->GetControlValue<int32>(Keys[0], ValueType);
|
|
const int32 ReferenceValue = ValueType == ERigControlValueType::Initial ? 0 :
|
|
HierarchyBeingDebugged->GetControlValue<int32>(Keys[0], ERigControlValueType::Initial);
|
|
|
|
return FirstValue != ReferenceValue;
|
|
}),
|
|
FSimpleDelegate::CreateLambda([ValueType, Keys, HierarchyToChange]()
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("ResetValueToDefault", "Reset Value To Default"));
|
|
HierarchyToChange->Modify();
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
const int32 ReferenceValue = ValueType == ERigControlValueType::Initial ? 0 :
|
|
HierarchyToChange->GetControlValue<int32>(Keys[0], ERigControlValueType::Initial);
|
|
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<int32>(ReferenceValue), ValueType, true, true);
|
|
}
|
|
})
|
|
));
|
|
}
|
|
|
|
void FRigControlElementDetails::CreateVector2DValueWidgetRow(
|
|
IDetailCategoryBuilder& CategoryBuilder,
|
|
const FText& Label,
|
|
const FText& Tooltip,
|
|
ERigControlValueType ValueType,
|
|
TAttribute<EVisibility> Visibility)
|
|
{
|
|
const bool bCurrent = ValueType == ERigControlValueType::Current;
|
|
const bool bInitial = ValueType == ERigControlValueType::Initial;
|
|
const bool bShowToggle = (ValueType == ERigControlValueType::Minimum) || (ValueType == ERigControlValueType::Maximum);
|
|
|
|
TArray<FRigElementKey> Keys = GetElementKeys();
|
|
URigHierarchy* HierarchyBeingDebugged = GetHierarchyBeingDebugged();
|
|
URigHierarchy* HierarchyToChange = bCurrent ? HierarchyBeingDebugged : HierarchyBeingCustomized;
|
|
Keys = HierarchyBeingDebugged->SortKeys(Keys);
|
|
|
|
using SNumericVector2DInputBox = SNumericVectorInputBox<float, FVector2f, 2>;
|
|
TSharedPtr<SNumericVector2DInputBox> VectorInputBox;
|
|
|
|
FDetailWidgetRow& WidgetRow = CategoryBuilder.AddCustomRow(Label);
|
|
TAttribute<ECheckBoxState> ToggleXChecked, ToggleYChecked;
|
|
FOnCheckStateChanged OnToggleXChanged, OnToggleYChanged;
|
|
|
|
if(bShowToggle)
|
|
{
|
|
auto ToggleChecked = [ValueType, Keys, HierarchyBeingDebugged](int32 Index) -> ECheckBoxState
|
|
{
|
|
TOptional<bool> FirstValue;
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
if(const FRigControlElement* ControlElement = HierarchyBeingDebugged->Find<FRigControlElement>(Key))
|
|
{
|
|
if(ControlElement->Settings.LimitEnabled.Num() == 2)
|
|
{
|
|
const bool Value = ControlElement->Settings.LimitEnabled[Index].GetForValueType(ValueType);
|
|
if(FirstValue.IsSet())
|
|
{
|
|
if(FirstValue.GetValue() != Value)
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FirstValue = Value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!ensure(FirstValue.IsSet()))
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
|
|
return FirstValue.GetValue() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
};
|
|
|
|
ToggleXChecked = TAttribute<ECheckBoxState>::CreateLambda([ToggleChecked]() -> ECheckBoxState
|
|
{
|
|
return ToggleChecked(0);
|
|
});
|
|
|
|
ToggleYChecked = TAttribute<ECheckBoxState>::CreateLambda([ToggleChecked]() -> ECheckBoxState
|
|
{
|
|
return ToggleChecked(1);
|
|
});
|
|
|
|
auto OnToggleChanged = [ValueType, Keys, HierarchyToChange](ECheckBoxState InValue, int32 Index)
|
|
{
|
|
if(InValue == ECheckBoxState::Undetermined)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FScopedTransaction Transaction(LOCTEXT("ChangeLimitToggle", "Change Limit Toggle"));
|
|
HierarchyToChange->Modify();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
if(FRigControlElement* ControlElement = HierarchyToChange->Find<FRigControlElement>(Key))
|
|
{
|
|
if(ControlElement->Settings.LimitEnabled.Num() == 2)
|
|
{
|
|
ControlElement->Settings.LimitEnabled[Index].SetForValueType(ValueType, InValue == ECheckBoxState::Checked);
|
|
HierarchyToChange->SetControlSettings(ControlElement, ControlElement->Settings, true, true, true);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
OnToggleXChanged = FOnCheckStateChanged::CreateLambda([OnToggleChanged](ECheckBoxState InValue)
|
|
{
|
|
OnToggleChanged(InValue, 0);
|
|
});
|
|
|
|
OnToggleYChanged = FOnCheckStateChanged::CreateLambda([OnToggleChanged](ECheckBoxState InValue)
|
|
{
|
|
OnToggleChanged(InValue, 1);
|
|
});
|
|
}
|
|
|
|
auto GetValue = [ValueType, Keys, HierarchyBeingDebugged](int32 Component) -> TOptional<float>
|
|
{
|
|
const float FirstValue = HierarchyBeingDebugged->GetControlValue<FVector3f>(Keys[0], ValueType).Component(Component);
|
|
for(int32 Index = 1; Index < Keys.Num(); Index++)
|
|
{
|
|
const float SecondValue = HierarchyBeingDebugged->GetControlValue<FVector3f>(Keys[Index], ValueType).Component(Component);
|
|
if(FirstValue != SecondValue)
|
|
{
|
|
return TOptional<float>();
|
|
}
|
|
}
|
|
return FirstValue;
|
|
};
|
|
|
|
auto OnValueChanged = [ValueType, Keys, HierarchyBeingDebugged, HierarchyToChange, this]
|
|
(TOptional<float> InValue, ETextCommit::Type InCommitType, bool bSetupUndo, int32 Component)
|
|
{
|
|
if(!InValue.IsSet())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const float Value = InValue.GetValue();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
FVector3f Vector = HierarchyBeingDebugged->GetControlValue<FVector3f>(Key, ValueType);
|
|
if(!FMath::IsNearlyEqual(Vector.Component(Component), Value))
|
|
{
|
|
if(!SliderTransaction.IsValid())
|
|
{
|
|
SliderTransaction = MakeShareable(new FScopedTransaction(NSLOCTEXT("ControlRigElementDetails", "ChangeValue", "Change Value")));
|
|
HierarchyToChange->Modify();
|
|
}
|
|
Vector.Component(Component) = Value;
|
|
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<FVector3f>(Vector), ValueType, bSetupUndo, bSetupUndo);
|
|
};
|
|
}
|
|
|
|
if(bSetupUndo)
|
|
{
|
|
SliderTransaction.Reset();
|
|
}
|
|
};
|
|
|
|
WidgetRow
|
|
.Visibility(Visibility)
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(Label)
|
|
.ToolTipText(Tooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SAssignNew(VectorInputBox, SNumericVector2DInputBox)
|
|
.Font(FAppStyle::GetFontStyle(TEXT("MenuItem.Font")))
|
|
.AllowSpin(ValueType == ERigControlValueType::Current || ValueType == ERigControlValueType::Initial)
|
|
.SpinDelta(0.01f)
|
|
.X_Lambda([GetValue]() -> TOptional<float>
|
|
{
|
|
return GetValue(0);
|
|
})
|
|
.Y_Lambda([GetValue]() -> TOptional<float>
|
|
{
|
|
return GetValue(1);
|
|
})
|
|
.OnXChanged_Lambda([OnValueChanged](TOptional<float> InValue)
|
|
{
|
|
OnValueChanged(InValue, ETextCommit::Default, false, 0);
|
|
})
|
|
.OnYChanged_Lambda([OnValueChanged](TOptional<float> InValue)
|
|
{
|
|
OnValueChanged(InValue, ETextCommit::Default, false, 1);
|
|
})
|
|
.OnXCommitted_Lambda([OnValueChanged](TOptional<float> InValue, ETextCommit::Type InCommitType)
|
|
{
|
|
OnValueChanged(InValue, InCommitType, true, 0);
|
|
})
|
|
.OnYCommitted_Lambda([OnValueChanged](TOptional<float> InValue, ETextCommit::Type InCommitType)
|
|
{
|
|
OnValueChanged(InValue, InCommitType, true, 1);
|
|
})
|
|
.DisplayToggle(bShowToggle)
|
|
.ToggleXChecked(ToggleXChecked)
|
|
.ToggleYChecked(ToggleYChecked)
|
|
.OnToggleXChanged(OnToggleXChanged)
|
|
.OnToggleYChanged(OnToggleYChanged)
|
|
]
|
|
.CopyAction(FUIAction(
|
|
FExecuteAction::CreateLambda([ValueType, Keys, HierarchyBeingDebugged]()
|
|
{
|
|
const FVector3f Data3 = HierarchyBeingDebugged->GetControlValue<FVector3f>(Keys[0], ValueType);
|
|
const FVector2f Data(Data3.X, Data3.Y);
|
|
FString Content = Data.ToString();
|
|
FPlatformApplicationMisc::ClipboardCopy(*Content);
|
|
}),
|
|
FCanExecuteAction())
|
|
)
|
|
.PasteAction(FUIAction(
|
|
FExecuteAction::CreateLambda([ValueType, Keys, HierarchyToChange]()
|
|
{
|
|
FString Content;
|
|
FPlatformApplicationMisc::ClipboardPaste(Content);
|
|
if(Content.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FVector2f Data = FVector2f::ZeroVector;
|
|
Data.InitFromString(Content);
|
|
|
|
FVector3f Data3(Data.X, Data.Y, 0);
|
|
|
|
FScopedTransaction Transaction(NSLOCTEXT("ControlRigElementDetails", "ChangeValue", "Change Value"));
|
|
HierarchyToChange->Modify();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<FVector3f>(Data3), ValueType, true, true);
|
|
}
|
|
}),
|
|
FCanExecuteAction())
|
|
);
|
|
|
|
if(ValueType == ERigControlValueType::Current || ValueType == ERigControlValueType::Initial)
|
|
{
|
|
WidgetRow.OverrideResetToDefault(FResetToDefaultOverride::Create(
|
|
TAttribute<bool>::CreateLambda([ValueType, Keys, HierarchyBeingDebugged]() -> bool
|
|
{
|
|
const FVector3f FirstValue = HierarchyBeingDebugged->GetControlValue<FVector3f>(Keys[0], ValueType);
|
|
const FVector3f ReferenceValue = ValueType == ERigControlValueType::Initial ? FVector3f::ZeroVector :
|
|
HierarchyBeingDebugged->GetControlValue<FVector3f>(Keys[0], ERigControlValueType::Initial);
|
|
|
|
return !(FirstValue - ReferenceValue).IsNearlyZero();
|
|
}),
|
|
FSimpleDelegate::CreateLambda([ValueType, Keys, HierarchyToChange]()
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("ResetValueToDefault", "Reset Value To Default"));
|
|
HierarchyToChange->Modify();
|
|
|
|
for(const FRigElementKey& Key : Keys)
|
|
{
|
|
const FVector3f ReferenceValue = ValueType == ERigControlValueType::Initial ? FVector3f::ZeroVector :
|
|
HierarchyToChange->GetControlValue<FVector3f>(Keys[0], ERigControlValueType::Initial);
|
|
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<FVector3f>(ReferenceValue), ValueType, true, true);
|
|
}
|
|
})
|
|
));
|
|
}
|
|
}
|
|
|
|
void FRigNullElementDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
FRigTransformElementDetails::CustomizeDetails(DetailBuilder);
|
|
CustomizeTransform(DetailBuilder);
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|