Files
UnrealEngine/Engine/Plugins/Animation/ControlRig/Source/ControlRigEditor/Private/ControlRigElementDetails.h
2025-05-18 13:04:45 +08:00

949 lines
32 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "IDetailCustomization.h"
#include "IPropertyTypeCustomization.h"
#include "Rigs/RigHierarchyDefines.h"
#include "Rigs/RigHierarchy.h"
#include "ControlRig.h"
#include "ControlRigBlueprint.h"
#include "Editor/ControlRigWrapperObject.h"
#include "Widgets/SRigVMGraphPinNameListValueWidget.h"
#include "Editor/SControlRigGizmoNameList.h"
#include "Styling/SlateTypes.h"
#include "IPropertyUtilities.h"
#include "SSearchableComboBox.h"
#include "Widgets/Input/SSegmentedControl.h"
#include "Widgets/Input/SMenuAnchor.h"
#include "DetailCategoryBuilder.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "SAdvancedTransformInputBox.h"
#include "HAL/PlatformApplicationMisc.h"
#include "Internationalization/FastDecimalFormat.h"
#include "ScopedTransaction.h"
#include "Styling/AppStyle.h"
#include "Algo/Transform.h"
#include "Editor/SRigHierarchyTreeView.h"
#include "ControlRigElementDetails.generated.h"
class IPropertyHandle;
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;
};
class SRigElementKeyWidget : public SCompoundWidget
{
public:
DECLARE_DELEGATE_RetVal(FText, FGetElementNameAsText);
DECLARE_DELEGATE_RetVal(ERigElementType, FGetElementType);
DECLARE_DELEGATE_RetVal(bool, FIsEnabled);
DECLARE_DELEGATE_OneParam(FOnElementTypeChanged, ERigElementType);
SLATE_BEGIN_ARGS(SRigElementKeyWidget)
{
}
SLATE_ARGUMENT(UControlRigBlueprint*, Blueprint)
SLATE_ARGUMENT(FSlateColor, ActiveBackgroundColor)
SLATE_ARGUMENT(FSlateColor, InactiveBackgroundColor)
SLATE_ARGUMENT(FSlateColor, ActiveForegroundColor)
SLATE_ARGUMENT(FSlateColor, InactiveForegroundColor)
SLATE_EVENT(SSearchableComboBox::FOnSelectionChanged, OnElementNameChanged)
SLATE_EVENT(FOnClicked, OnGetSelectedClicked)
SLATE_EVENT(FOnClicked, OnSelectInHierarchyClicked)
SLATE_EVENT(FGetElementNameAsText, OnGetElementNameAsText)
SLATE_EVENT(FGetElementType, OnGetElementType)
SLATE_EVENT(FOnElementTypeChanged, OnElementTypeChanged)
SLATE_EVENT(FIsEnabled, IsEnabled);
SLATE_END_ARGS()
void Construct(const FArguments& InArgs, TSharedPtr<IPropertyHandle> InNameHandle, TSharedPtr<IPropertyHandle> InTypeHandle);
void Construct(const FArguments& InArgs);
void UpdateElementNameList();
private:
TSharedPtr<IPropertyHandle> NameHandle;
TSharedPtr<IPropertyHandle> TypeHandle;
/** Helper buttons. */
TSharedPtr<SButton> UseSelectedButton;
TSharedPtr<SButton> SelectElementButton;
FGetElementType OnGetElementType;
FOnElementTypeChanged OnElementTypeChanged;
SSearchableComboBox::FOnSelectionChanged OnElementNameChanged;
UControlRigBlueprint* BlueprintBeingCustomized;
TArray<TSharedPtr<FString>> ElementNameList;
TSharedPtr<SSearchableComboBox> SearchableComboBox;
};
class FRigElementKeyDetails : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance()
{
return MakeShareable(new FRigElementKeyDetails);
}
/** IPropertyTypeCustomization interface */
virtual void CustomizeHeader(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
protected:
ERigElementType GetElementType() const;
FString GetElementName() const;
void SetElementName(FString InName);
void OnElementNameChanged(TSharedPtr<FString> InItem, ESelectInfo::Type InSelectionInfo);
FText GetElementNameAsText() const;
/** Helper buttons. */
TSharedPtr<SButton> UseSelectedButton;
TSharedPtr<SButton> SelectElementButton;
public:
static FSlateColor OnGetWidgetForeground(const TSharedPtr<SButton> Button);
static FSlateColor OnGetWidgetBackground(const TSharedPtr<SButton> Button);
protected:
FReply OnGetSelectedClicked();
FReply OnSelectInHierarchyClicked();
TSharedPtr<IPropertyHandle> TypeHandle;
TSharedPtr<IPropertyHandle> NameHandle;
UControlRigBlueprint* BlueprintBeingCustomized;
TSharedPtr<SRigElementKeyWidget> RigElementKeyWidget;
};
class FRigComponentKeyDetails : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance()
{
return MakeShareable(new FRigComponentKeyDetails);
}
/** IPropertyTypeCustomization interface */
virtual void CustomizeHeader(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
protected:
FRigElementKey GetElementKey() const;
FString GetComponentName() const;
void SetComponentName(FString InName);
void OnComponentNameChanged(TSharedPtr<FString> InItem, ESelectInfo::Type InSelectionInfo);
FText GetComponentNameAsText() const;
void UpdateComponentNameList();
TSharedPtr<IPropertyHandle> ElementKeyHandle;
TSharedPtr<IPropertyHandle> NameHandle;
UControlRigBlueprint* BlueprintBeingCustomized;
TArray<TSharedPtr<FString>> ComponentNameList;
TSharedPtr<SSearchableComboBox> SearchableComboBox;
};
UENUM()
enum class ERigElementDetailsTransformComponent : uint8
{
TranslationX,
TranslationY,
TranslationZ,
RotationRoll,
RotationPitch,
RotationYaw,
ScaleX,
ScaleY,
ScaleZ
};
class FRigComputedTransformDetails : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance()
{
return MakeShareable(new FRigComputedTransformDetails);
}
/** IPropertyTypeCustomization interface */
virtual void CustomizeHeader(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
protected:
TSharedPtr<IPropertyHandle> TransformHandle;
FEditPropertyChain PropertyChain;
UControlRigBlueprint* BlueprintBeingCustomized;
void OnTransformChanged(FEditPropertyChain* InPropertyChain);
};
class FRigControlTransformChannelDetails : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance()
{
return MakeShareable(new FRigControlTransformChannelDetails);
}
/** IPropertyTypeCustomization interface */
virtual void CustomizeHeader(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
protected:
ERigControlTransformChannel GetChannel() const;
int32 GetChannelAsInt32() const { return (int32)GetChannel(); }
void OnChannelChanged(int32 NewSelection, ESelectInfo::Type);
public:
static const TArray<ERigControlTransformChannel>* GetVisibleChannelsForControlType(ERigControlType InControlType);
private:
TSharedPtr<IPropertyHandle> Handle;
};
class FRigBaseElementDetails : public IDetailCustomization
{
public:
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
virtual void BeginDestroy() {};
virtual void PendingDelete() override;
FRigElementKey GetElementKey() const;
FText GetName() const;
void SetName(const FText& InNewText, ETextCommit::Type InCommitType);
bool OnVerifyNameChanged(const FText& InText, FText& OutErrorMessage);
void OnStructContentsChanged(FProperty* InProperty, const TSharedRef<IPropertyUtilities> PropertyUtilities);
bool IsConstructionModeEnabled() const;
FText GetParentElementName() const;
TArray<FRigElementKey> GetElementKeys() const;
template<typename T>
TArray<T> GetElementsInDetailsView(const TArray<FRigElementKey>& InFilter = TArray<FRigElementKey>()) const
{
TArray<T> Elements;
for(const FPerElementInfo& Info : PerElementInfos)
{
T Content = Info.WrapperObject->GetContent<T>();
if(!InFilter.IsEmpty() && !InFilter.Contains(Content.GetKey()))
{
continue;
}
Elements.Add(Content);
}
return Elements;
}
struct FPerElementInfo
{
FPerElementInfo()
: WrapperObject()
, Element()
, DefaultElement()
{}
bool IsValid() const { return Element.IsValid(); }
bool IsProcedural() const { return Element.IsValid() && Element.Get()->IsProcedural(); }
operator bool() const { return IsValid(); }
URigHierarchy* GetHierarchy() const { return (URigHierarchy*)Element.GetHierarchy(); }
URigHierarchy* GetDefaultHierarchy() const
{
if(DefaultElement.IsValid())
{
return (URigHierarchy*)DefaultElement.GetHierarchy();
}
return GetHierarchy();
}
UControlRigBlueprint* GetBlueprint() const
{
if(const UControlRig* ControlRig = GetHierarchy()->GetTypedOuter<UControlRig>())
{
return Cast<UControlRigBlueprint>(ControlRig->GetClass()->ClassGeneratedBy);
}
return GetDefaultHierarchy()->GetTypedOuter<UControlRigBlueprint>();
}
template<typename T = FRigBaseElement>
T* GetElement() const
{
return (T*)Element.Get<T>();
}
template<typename T = FRigBaseElement>
T* GetDefaultElement() const
{
if(DefaultElement)
{
return (T*)DefaultElement.Get<T>();
}
return GetElement<T>();
}
TWeakObjectPtr<URigVMDetailsViewWrapperObject> WrapperObject;
FRigElementHandle Element;
FRigElementHandle DefaultElement;
};
const FPerElementInfo& FindElement(const FRigElementKey& InKey) const;
bool IsAnyElementOfType(ERigElementType InType) const;
bool IsAnyElementNotOfType(ERigElementType InType) const;
bool IsAnyControlOfAnimationType(ERigControlAnimationType InType) const;
bool IsAnyControlNotOfAnimationType(ERigControlAnimationType InType) const;
bool IsAnyControlOfValueType(ERigControlType InType) const;
bool IsAnyControlNotOfValueType(ERigControlType InType) const;
bool IsAnyElementProcedural() const;
bool IsAnyConnectorImported() const;
bool IsAnyConnectorPrimary() const;
bool GetCommonElementType(ERigElementType& OutElementType) const;
bool GetCommonControlType(ERigControlType& OutControlType) const;
bool GetCommonAnimationType(ERigControlAnimationType& OutAnimationType) const;
const FPerElementInfo* FindElementByPredicate(const TFunction<bool(const FPerElementInfo&)>& InPredicate) const;
bool ContainsElementByPredicate(const TFunction<bool(const FPerElementInfo&)>& InPredicate) const;
static void RegisterSectionMappings(FPropertyEditorModule& PropertyEditorModule);
virtual void RegisterSectionMappings(FPropertyEditorModule& PropertyEditorModule, UClass* InClass);
FReply OnSelectParentElementInHierarchyClicked();
FReply OnSelectElementClicked(const FRigElementKey& InKey);
protected:
void CustomizeComponents(IDetailLayoutBuilder& DetailBuilder);
void CustomizeMetadata(IDetailLayoutBuilder& DetailBuilder);
TArray<FPerElementInfo> PerElementInfos;
FDelegateHandle MetadataHandle;
TArray<FControlRigInteractionScope*> InteractionScopes;
struct FSharedComponent
{
FRigComponentHandle Handle;
const UScriptStruct* ScriptStruct = nullptr;
bool bIsProcedural = false;
};
TArray<FSharedComponent> SharedComponents;
TSharedPtr<SButton> SelectParentElementButton;
};
namespace ERigTransformElementDetailsTransform
{
enum Type
{
Initial,
Current,
Offset,
Minimum,
Maximum,
Max
};
}
class FRigTransformElementDetails : public FRigBaseElementDetails
{
public:
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
/** FRigBaseElementDetails interface */
virtual void RegisterSectionMappings(FPropertyEditorModule& PropertyEditorModule, UClass* InClass) override;
virtual void CustomizeTransform(IDetailLayoutBuilder& DetailBuilder);
protected:
bool IsCurrentLocalEnabled() const;
void AddChoiceWidgetRow(IDetailCategoryBuilder& InCategory, const FText& InSearchText, TSharedRef<SWidget> InWidget);
protected:
FDetailWidgetRow& CreateTransformComponentValueWidgetRow(
ERigControlType InControlType,
const TArray<FRigElementKey>& Keys,
SAdvancedTransformInputBox<FEulerTransform>::FArguments TransformWidgetArgs,
IDetailCategoryBuilder& CategoryBuilder,
const FText& Label,
const FText& Tooltip,
ERigTransformElementDetailsTransform::Type CurrentTransformType,
ERigControlValueType ValueType,
TSharedPtr<SWidget> NameContent = TSharedPtr<SWidget>());
FDetailWidgetRow& CreateEulerTransformValueWidgetRow(
const TArray<FRigElementKey>& Keys,
SAdvancedTransformInputBox<FEulerTransform>::FArguments TransformWidgetArgs,
IDetailCategoryBuilder& CategoryBuilder,
const FText& Label,
const FText& Tooltip,
ERigTransformElementDetailsTransform::Type CurrentTransformType,
ERigControlValueType ValueType,
TSharedPtr<SWidget> NameContent = TSharedPtr<SWidget>());
static ERigTransformElementDetailsTransform::Type GetTransformTypeFromValueType(ERigControlValueType InValueType);
private:
static TSharedPtr<TArray<ERigTransformElementDetailsTransform::Type>> PickedTransforms;
protected:
TSharedPtr<FScopedTransaction> SliderTransaction;
};
class FRigBoneElementDetails : public FRigTransformElementDetails
{
public:
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
static TSharedRef<IDetailCustomization> MakeInstance()
{
return MakeShareable(new FRigBoneElementDetails);
}
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
};
class FRigControlElementDetails : public FRigTransformElementDetails
{
public:
// Makes a new instance of this detail layout class for a specific detail view requesting it
static TSharedRef<IDetailCustomization> MakeInstance()
{
return MakeShareable(new FRigControlElementDetails);
}
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
void CustomizeValue(IDetailLayoutBuilder& DetailBuilder);
void CustomizeControl(IDetailLayoutBuilder& DetailBuilder);
void CustomizeAnimationChannels(IDetailLayoutBuilder& DetailBuilder);
void CustomizeAvailableSpaces(IDetailLayoutBuilder& DetailBuilder);
void CustomizeShape(IDetailLayoutBuilder& DetailBuilder);
virtual void BeginDestroy() override;
/** FRigBaseElementDetails interface */
virtual void RegisterSectionMappings(FPropertyEditorModule& PropertyEditorModule, UClass* InClass) override;
bool IsShapeEnabled() const;
const TArray<TSharedPtr<FRigVMStringWithTag>>& GetShapeNameList() const;
FText GetDisplayName() const;
void SetDisplayName(const FText& InNewText, ETextCommit::Type InCommitType);
FText GetDisplayNameForElement(const FRigElementKey& InKey) const;
void SetDisplayNameForElement(const FText& InNewText, ETextCommit::Type InCommitType, const FRigElementKey& InKeyToRename);
bool OnVerifyDisplayNameChanged(const FText& InText, FText& OutErrorMessage, const FRigElementKey& InKeyToRename);
void OnCopyShapeProperties();
void OnPasteShapeProperties();
FDetailWidgetRow& CreateBoolValueWidgetRow(
const TArray<FRigElementKey>& Keys,
IDetailCategoryBuilder& CategoryBuilder,
const FText& Label,
const FText& Tooltip,
ERigControlValueType ValueType,
TAttribute<EVisibility> Visibility = EVisibility::Visible,
TSharedPtr<SWidget> NameContent = TSharedPtr<SWidget>());
FDetailWidgetRow& CreateFloatValueWidgetRow(
const TArray<FRigElementKey>& Keys,
IDetailCategoryBuilder& CategoryBuilder,
const FText& Label,
const FText& Tooltip,
ERigControlValueType ValueType,
TAttribute<EVisibility> Visibility = EVisibility::Visible,
TSharedPtr<SWidget> NameContent = TSharedPtr<SWidget>());
FDetailWidgetRow& CreateIntegerValueWidgetRow(
const TArray<FRigElementKey>& Keys,
IDetailCategoryBuilder& CategoryBuilder,
const FText& Label,
const FText& Tooltip,
ERigControlValueType ValueType,
TAttribute<EVisibility> Visibility = EVisibility::Visible,
TSharedPtr<SWidget> NameContent = TSharedPtr<SWidget>());
FDetailWidgetRow& CreateEnumValueWidgetRow(
const TArray<FRigElementKey>& Keys,
IDetailCategoryBuilder& CategoryBuilder,
const FText& Label,
const FText& Tooltip,
ERigControlValueType ValueType,
TAttribute<EVisibility> Visibility = EVisibility::Visible,
TSharedPtr<SWidget> NameContent = TSharedPtr<SWidget>());
FDetailWidgetRow& CreateVector2DValueWidgetRow(
const TArray<FRigElementKey>& Keys,
IDetailCategoryBuilder& CategoryBuilder,
const FText& Label,
const FText& Tooltip,
ERigControlValueType ValueType,
TAttribute<EVisibility> Visibility = EVisibility::Visible,
TSharedPtr<SWidget> NameContent = TSharedPtr<SWidget>());
// this is a template since we specialize it further down for
// a 'nearly equals' implementation for floats and math types.
template<typename T>
static bool Equals(const T& A, const T& B)
{
return A == B;
}
private:
template<typename T>
FDetailWidgetRow& CreateNumericValueWidgetRow(
const TArray<FRigElementKey>& Keys,
IDetailCategoryBuilder& CategoryBuilder,
const FText& Label,
const FText& Tooltip,
ERigControlValueType ValueType,
TAttribute<EVisibility> Visibility = EVisibility::Visible,
TSharedPtr<SWidget> NameContent = TSharedPtr<SWidget>())
{
const bool bShowToggle = (ValueType == ERigControlValueType::Minimum) || (ValueType == ERigControlValueType::Maximum);
const bool bIsProcedural = IsAnyElementProcedural();
const bool bIsEnabled = !bIsProcedural || ValueType == ERigControlValueType::Current;
URigHierarchy* Hierarchy = PerElementInfos[0].GetHierarchy();
URigHierarchy* HierarchyToChange = PerElementInfos[0].GetDefaultHierarchy();
if(ValueType == ERigControlValueType::Current)
{
HierarchyToChange = Hierarchy;
}
TSharedPtr<SNumericEntryBox<T>> NumericEntryBox;
FDetailWidgetRow& WidgetRow = CategoryBuilder.AddCustomRow(Label);
TAttribute<ECheckBoxState> ToggleChecked;
FOnCheckStateChanged OnToggleChanged;
if(bShowToggle)
{
ToggleChecked = TAttribute<ECheckBoxState>::CreateLambda(
[ValueType, Keys, Hierarchy]() -> ECheckBoxState
{
TOptional<bool> FirstValue;
for(const FRigElementKey& Key : Keys)
{
if(const FRigControlElement* ControlElement = Hierarchy->Find<FRigControlElement>(Key))
{
if(ControlElement->Settings.LimitEnabled.Num() == 1)
{
const bool Value = ControlElement->Settings.LimitEnabled[0].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;
});
OnToggleChanged = FOnCheckStateChanged::CreateLambda(
[ValueType, Keys, HierarchyToChange](ECheckBoxState InValue)
{
if(InValue == ECheckBoxState::Undetermined)
{
return;
}
FScopedTransaction Transaction(NSLOCTEXT("ControlRigElementDetails", "ChangeLimitToggle", "Change Limit Toggle"));
for(const FRigElementKey& Key : Keys)
{
if(FRigControlElement* ControlElement = HierarchyToChange->Find<FRigControlElement>(Key))
{
if(ControlElement->Settings.LimitEnabled.Num() == 1)
{
HierarchyToChange->Modify();
ControlElement->Settings.LimitEnabled[0].SetForValueType(ValueType, InValue == ECheckBoxState::Checked);
HierarchyToChange->SetControlSettings(ControlElement, ControlElement->Settings, true, true, true);
}
}
}
});
}
auto OnValueChanged = [ValueType, Keys, HierarchyToChange, this]
(TOptional<T> InValue, ETextCommit::Type InCommitType, bool bSetupUndo)
{
if(!InValue.IsSet())
{
return;
}
const T Value = InValue.GetValue();
for(const FRigElementKey& Key : Keys)
{
const T PreviousValue = HierarchyToChange->GetControlValue(Key, ValueType).Get<T>();
if(!Equals(PreviousValue, Value))
{
if(!SliderTransaction.IsValid())
{
SliderTransaction = MakeShareable(new FScopedTransaction(NSLOCTEXT("ControlRigElementDetails", "ChangeValue", "Change Value")));
HierarchyToChange->Modify();
}
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<T>(Value), ValueType, bSetupUndo, bSetupUndo);
}
}
if(bSetupUndo)
{
SliderTransaction.Reset();
}
};
if(!NameContent.IsValid())
{
SAssignNew(NameContent, STextBlock)
.Text(Label)
.ToolTipText(Tooltip)
.Font(IDetailLayoutBuilder::GetDetailFont())
.IsEnabled(bIsEnabled);
}
WidgetRow
.Visibility(Visibility)
.NameContent()
.MinDesiredWidth(200.f)
.MaxDesiredWidth(800.f)
[
NameContent.ToSharedRef()
]
.ValueContent()
[
SAssignNew(NumericEntryBox, SNumericEntryBox<T>)
.Font(FAppStyle::GetFontStyle(TEXT("MenuItem.Font")))
.AllowSpin(ValueType == ERigControlValueType::Current || ValueType == ERigControlValueType::Initial)
.LinearDeltaSensitivity(1)
.Delta(0.01f)
.Value_Lambda([ValueType, Keys, Hierarchy]() -> TOptional<T>
{
const T FirstValue = Hierarchy->GetControlValue<T>(Keys[0], ValueType);
for(int32 Index = 1; Index < Keys.Num(); Index++)
{
const T SecondValue = Hierarchy->GetControlValue<T>(Keys[Index], ValueType);
if(FirstValue != SecondValue)
{
return TOptional<T>();
}
}
return FirstValue;
})
.OnValueChanged_Lambda([ValueType, Keys, HierarchyToChange, OnValueChanged](TOptional<T> InValue)
{
OnValueChanged(InValue, ETextCommit::Default, false);
})
.OnValueCommitted_Lambda([ValueType, Keys, HierarchyToChange, OnValueChanged](TOptional<T> InValue, ETextCommit::Type InCommitType)
{
OnValueChanged(InValue, InCommitType, true);
})
.MinSliderValue_Lambda([ValueType, Keys, Hierarchy]() -> TOptional<T>
{
if(ValueType == ERigControlValueType::Current || ValueType == ERigControlValueType::Initial)
{
return Hierarchy->GetControlValue<T>(Keys[0], ERigControlValueType::Minimum);
}
return TOptional<T>();
})
.MaxSliderValue_Lambda([ValueType, Keys, Hierarchy]() -> TOptional<T>
{
if(ValueType == ERigControlValueType::Current || ValueType == ERigControlValueType::Initial)
{
return Hierarchy->GetControlValue<T>(Keys[0], ERigControlValueType::Maximum);
}
return TOptional<T>();
})
.DisplayToggle(bShowToggle)
.ToggleChecked(ToggleChecked)
.OnToggleChanged(OnToggleChanged)
.UndeterminedString(NSLOCTEXT("FRigControlElementDetails", "MultipleValues", "Multiple Values"))
.IsEnabled(bIsEnabled)
.PreventThrottling(true)
]
.CopyAction(FUIAction(
FExecuteAction::CreateLambda([ValueType, Keys, Hierarchy]()
{
const T FirstValue = Hierarchy->GetControlValue<T>(Keys[0], ValueType);
const FString Content = FastDecimalFormat::NumberToString(
FirstValue,
FastDecimalFormat::GetCultureAgnosticFormattingRules(),
FNumberFormattingOptions());
FPlatformApplicationMisc::ClipboardCopy(*Content);
}),
FCanExecuteAction())
)
.PasteAction(FUIAction(
FExecuteAction::CreateLambda([ValueType, Keys, HierarchyToChange]()
{
FString Content;
FPlatformApplicationMisc::ClipboardPaste(Content);
if(Content.IsEmpty())
{
return;
}
T Value = T(0);
if(FastDecimalFormat::StringToNumber(
*Content,
Content.Len(),
FastDecimalFormat::GetCultureAgnosticFormattingRules(),
FNumberParsingOptions(),
Value))
{
FScopedTransaction Transaction(NSLOCTEXT("ControlRigElementDetails", "ChangeValue", "Change Value"));
for(const FRigElementKey& Key : Keys)
{
HierarchyToChange->Modify();
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<T>(Value), ValueType, true, true);
}
}
}),
FCanExecuteAction::CreateLambda([bIsEnabled]() { return bIsEnabled; }))
);
if((ValueType == ERigControlValueType::Current || ValueType == ERigControlValueType::Initial) && bIsEnabled)
{
WidgetRow.OverrideResetToDefault(FResetToDefaultOverride::Create(
TAttribute<bool>::CreateLambda([ValueType, Keys, Hierarchy]() -> bool
{
const T FirstValue = Hierarchy->GetControlValue<T>(Keys[0], ValueType);
const T ReferenceValue = ValueType == ERigControlValueType::Initial ? T(0) :
Hierarchy->GetControlValue<T>(Keys[0], ERigControlValueType::Initial);
return !FRigControlElementDetails::Equals(FirstValue, ReferenceValue);
}),
FSimpleDelegate::CreateLambda([ValueType, Keys, HierarchyToChange]()
{
FScopedTransaction Transaction(NSLOCTEXT("ControlRigElementDetails", "ResetValueToDefault", "Reset Value To Default"));
for(const FRigElementKey& Key : Keys)
{
const T ReferenceValue = ValueType == ERigControlValueType::Initial ? T(0) :
HierarchyToChange->GetControlValue<T>(Keys[0], ERigControlValueType::Initial);
HierarchyToChange->Modify();
HierarchyToChange->SetControlValue(Key, FRigControlValue::Make<T>(ReferenceValue), ValueType, true, true);
}
})
));
}
return WidgetRow;
}
// animation channel related callbacks
FReply OnAddAnimationChannelClicked();
TSharedRef<ITableRow> HandleGenerateAnimationChannelTypeRow(TSharedPtr<ERigControlType> ControlType, const TSharedRef<STableViewBase>& OwnerTable, FRigElementKey ControlKey);
// multi parent related callbacks
const FRigTreeDisplaySettings& GetDisplaySettings() const { return DisplaySettings; }
TSharedRef<SWidget> GetAddSpaceContent(const TSharedRef<IPropertyUtilities> PropertyUtilities);
FReply OnAddSpaceMouseDown(const FGeometry& InGeometry, const FPointerEvent& InPointerEvent, const TSharedRef<IPropertyUtilities> PropertyUtilities);
void OnAddSpaceSelection(TSharedPtr<FRigTreeElement> Selection, ESelectInfo::Type SelectInfo, const TSharedRef<IPropertyUtilities> PropertyUtilities);
void HandleControlTypeChanged(TSharedPtr<ERigControlType> ControlType, ESelectInfo::Type SelectInfo, FRigElementKey ControlKey, const TSharedRef<IPropertyUtilities> PropertyUtilities);
void HandleControlTypeChanged(ERigControlType ControlType, TArray<FRigElementKey> ControlKeys, const TSharedRef<IPropertyUtilities> PropertyUtilities);
void HandleControlEnumChanged(TSharedPtr<FString> InItem, ESelectInfo::Type InSelectionInfo, const TSharedRef<IPropertyUtilities> PropertyUtilities);
TArray<TSharedPtr<FRigVMStringWithTag>> ShapeNameList;
TSharedPtr<FRigInfluenceEntryModifier> InfluenceModifier;
TSharedPtr<FStructOnScope> InfluenceModifierStruct;
TSharedPtr<IPropertyHandle> ShapeNameHandle;
TSharedPtr<IPropertyHandle> ShapeColorHandle;
TSharedPtr<SControlRigShapeNameList> ShapeNameListWidget;
static TSharedPtr<TArray<ERigControlValueType>> PickedValueTypes;
FRigTreeDisplaySettings DisplaySettings;
TSharedPtr<SMenuAnchor> AddSpaceMenuAnchor;
};
template<>
inline bool FRigControlElementDetails::Equals<float>(const float& A, const float& B)
{
return FMath::IsNearlyEqual(A, B);
}
template<>
inline bool FRigControlElementDetails::Equals<double>(const double& A, const double& B)
{
return FMath::IsNearlyEqual(A, B);
}
template<>
inline bool FRigControlElementDetails::Equals<FVector>(const FVector& A, const FVector& B)
{
return (A - B).IsNearlyZero();
}
template<>
inline bool FRigControlElementDetails::Equals<FRotator>(const FRotator& A, const FRotator& B)
{
return (A - B).IsNearlyZero();
}
template<>
inline bool FRigControlElementDetails::Equals<FEulerTransform>(const FEulerTransform& A, const FEulerTransform& B)
{
return Equals(A.Location, B.Location) && Equals(A.Rotation, B.Rotation) && Equals(A.Scale, B.Scale);
}
class FRigNullElementDetails : public FRigTransformElementDetails
{
public:
// Makes a new instance of this detail layout class for a specific detail view requesting it
static TSharedRef<IDetailCustomization> MakeInstance()
{
return MakeShareable(new FRigNullElementDetails);
}
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
};
class FRigConnectorElementDetails : public FRigTransformElementDetails
{
public:
// Makes a new instance of this detail layout class for a specific detail view requesting it
static TSharedRef<IDetailCustomization> MakeInstance()
{
return MakeShareable(new FRigConnectorElementDetails);
}
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
void CustomizeSettings(IDetailLayoutBuilder& DetailBuilder);
void CustomizeConnectorTargets(IDetailLayoutBuilder& DetailBuilder);
private:
TOptional<EConnectorType> GetConnectorType() const;
TOptional<bool> GetIsConnectorArray() const;
bool IsArrayEnabled() const;
bool OnTargetsChanged(TArray<FRigElementKey> InTargets);
TSharedPtr<IPropertyHandle> ConnectorTypeHandle;
TSharedPtr<IPropertyHandle> IsArrayHandle;
};
class FRigSocketElementDetails : public FRigNullElementDetails
{
public:
// Makes a new instance of this detail layout class for a specific detail view requesting it
static TSharedRef<IDetailCustomization> MakeInstance()
{
return MakeShareable(new FRigSocketElementDetails);
}
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
void CustomizeSettings(IDetailLayoutBuilder& DetailBuilder);
private:
FReply SetSocketColor(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent);
FLinearColor GetSocketColor() const;
void OnSocketColorPicked(FLinearColor NewColor);
void SetSocketDescription(const FText& InDescription, ETextCommit::Type InCommitType);
FText GetSocketDescription() const;
};
class FRigConnectionRuleDetails : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance()
{
return MakeShareable(new FRigConnectionRuleDetails);
}
/** IPropertyTypeCustomization interface */
virtual void CustomizeHeader(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
protected:
TSharedRef<SWidget> GenerateStructPicker();
void OnPickedStruct(const UScriptStruct* ChosenStruct);
FText OnGetStructTextValue() const;
void OnRuleContentChanged();
FRigConnectionRuleStash RuleStash;
TSharedPtr<FStructOnScope> Storage;
UControlRigBlueprint* BlueprintBeingCustomized;
TSharedPtr<IPropertyHandle> StructPropertyHandle;
TSharedPtr<IPropertyUtilities> PropertyUtilities;
TAttribute<bool> EnabledAttribute;
};
class FRigBaseComponentDetails : public IDetailCustomization
{
public:
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
static TSharedRef<IDetailCustomization> MakeInstance()
{
return MakeShareable(new FRigBaseComponentDetails);
}
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
};