2023-11-09 05:26:12 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "ControlRigModuleDetails.h"
# include "Widgets/SWidget.h"
# include "IDetailChildrenBuilder.h"
# include "Widgets/Text/STextBlock.h"
# include "Widgets/Input/SEditableTextBox.h"
# include "Widgets/Text/SInlineEditableTextBlock.h"
# include "Widgets/Input/SVectorInputBox.h"
# include "Widgets/Input/SCheckBox.h"
# include "Widgets/Input/SButton.h"
2023-11-29 03:23:17 -05:00
# include "ModularRigController.h"
2023-11-09 05:26:12 -05:00
# include "ControlRigBlueprint.h"
2023-12-19 09:21:36 -05:00
# include "ControlRigEditorStyle.h"
2023-11-09 05:26:12 -05:00
# include "ControlRigElementDetails.h"
# include "Graph/ControlRigGraph.h"
# include "PropertyCustomizationHelpers.h"
# include "PropertyEditorModule.h"
# include "SEnumCombo.h"
# include "HAL/PlatformApplicationMisc.h"
# include "Styling/AppStyle.h"
2023-11-22 05:50:24 -05:00
# include "Editor/SModularRigTreeView.h"
2023-11-09 05:26:12 -05:00
# include "StructViewerFilter.h"
# include "StructViewerModule.h"
2023-11-21 12:05:15 -05:00
# include "Features/IModularFeatures.h"
# include "IPropertyAccessEditor.h"
2023-12-19 09:21:36 -05:00
# include "ModularRigRuleManager.h"
2023-11-21 12:05:15 -05:00
# include "ScopedTransaction.h"
2023-12-19 09:21:36 -05:00
# include "Editor/SRigHierarchyTreeView.h"
2024-05-24 04:04:04 -04:00
# include "Widgets/SRigVMVariantTagWidget.h"
2024-08-21 09:49:15 -04:00
# include "Algo/Sort.h"
2023-11-09 05:26:12 -05:00
# define LOCTEXT_NAMESPACE "ControlRigModuleDetails"
static const FText ControlRigModuleDetailsMultipleValues = LOCTEXT ( " MultipleValues " , " Multiple Values " ) ;
static void RigModuleDetails_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 ( const UControlRig * ControlRig = Object - > GetTypedOuter < UControlRig > ( ) )
{
OutBlueprint = Cast < UControlRigBlueprint > ( ControlRig - > GetClass ( ) - > ClassGeneratedBy ) ;
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 ;
}
}
}
}
static UControlRigBlueprint * RigModuleDetails_GetBlueprintFromRig ( UModularRig * InRig )
{
if ( InRig = = nullptr )
{
return nullptr ;
}
UControlRigBlueprint * Blueprint = InRig - > GetTypedOuter < UControlRigBlueprint > ( ) ;
if ( Blueprint = = nullptr )
{
Blueprint = Cast < UControlRigBlueprint > ( InRig - > GetClass ( ) - > ClassGeneratedBy ) ;
}
return Blueprint ;
}
void FRigModuleInstanceDetails : : CustomizeDetails ( IDetailLayoutBuilder & DetailBuilder )
{
PerModuleInfos . Reset ( ) ;
TArray < TWeakObjectPtr < UObject > > DetailObjects ;
DetailBuilder . GetObjectsBeingCustomized ( DetailObjects ) ;
for ( TWeakObjectPtr < UObject > DetailObject : DetailObjects )
{
2023-11-21 12:05:15 -05:00
if ( UControlRig * ModuleInstance = Cast < UControlRig > ( DetailObject ) )
2023-11-15 07:37:23 -05:00
{
2023-11-21 12:05:15 -05:00
if ( const UModularRig * ModularRig = Cast < UModularRig > ( ModuleInstance - > GetOuter ( ) ) )
2023-11-09 05:26:12 -05:00
{
2023-11-21 12:05:15 -05:00
if ( const FRigModuleInstance * Module = ModularRig - > FindModule ( ModuleInstance ) )
{
const FString Path = Module - > GetPath ( ) ;
FPerModuleInfo Info ;
Info . Path = Path ;
Info . Module = ModularRig - > GetHandle ( Path ) ;
if ( ! Info . Module . IsValid ( ) )
{
return ;
}
if ( const UControlRigBlueprint * Blueprint = Info . GetBlueprint ( ) )
{
if ( const UModularRig * DefaultModularRig = Cast < UModularRig > ( Blueprint - > GeneratedClass - > GetDefaultObject ( ) ) )
{
Info . DefaultModule = DefaultModularRig - > GetHandle ( Path ) ;
}
}
PerModuleInfos . Add ( Info ) ;
}
2023-11-09 05:26:12 -05:00
}
}
}
2023-11-21 12:05:15 -05:00
// don't customize if the
if ( PerModuleInfos . IsEmpty ( ) )
{
return ;
}
2023-11-09 05:26:12 -05:00
2023-12-07 06:01:49 -05:00
TArray < FName > OriginalCategoryNames ;
DetailBuilder . GetCategoryNames ( OriginalCategoryNames ) ;
2023-11-09 05:26:12 -05:00
IDetailCategoryBuilder & GeneralCategory = DetailBuilder . EditCategory ( TEXT ( " General " ) , LOCTEXT ( " General " , " General " ) ) ;
{
2023-12-07 05:11:51 -05:00
static const FText NameTooltip = LOCTEXT ( " NameTooltip " , " The name is used to determine the long name (the full path) and to provide a unique address within the rig. " ) ;
2023-11-09 05:26:12 -05:00
GeneralCategory . AddCustomRow ( FText : : FromString ( TEXT ( " Name " ) ) )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( FText : : FromString ( TEXT ( " Name " ) ) )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
2023-12-07 05:11:51 -05:00
. ToolTipText ( NameTooltip )
2023-11-09 05:26:12 -05:00
]
. ValueContent ( )
[
SNew ( SInlineEditableTextBlock )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
. Text ( this , & FRigModuleInstanceDetails : : GetName )
2023-12-07 05:11:51 -05:00
. OnTextCommitted ( this , & FRigModuleInstanceDetails : : SetName , DetailBuilder . GetPropertyUtilities ( ) )
. ToolTipText ( NameTooltip )
. OnVerifyTextChanged ( this , & FRigModuleInstanceDetails : : OnVerifyNameChanged )
] ;
static const FText ShortNameTooltip = LOCTEXT ( " ShortNameTooltip " , " The short name is used for the user interface, for example the sequencer channels. \n This value can be edited and adjusted as needed. " ) ;
GeneralCategory . AddCustomRow ( FText : : FromString ( TEXT ( " Short Name " ) ) )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( FText : : FromString ( TEXT ( " Short Name " ) ) )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
. ToolTipText ( ShortNameTooltip )
. IsEnabled ( PerModuleInfos . Num ( ) = = 1 )
]
. ValueContent ( )
[
SNew ( SInlineEditableTextBlock )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
. Text ( this , & FRigModuleInstanceDetails : : GetShortName )
. OnTextCommitted ( this , & FRigModuleInstanceDetails : : SetShortName , DetailBuilder . GetPropertyUtilities ( ) )
. ToolTipText ( ShortNameTooltip )
. IsEnabled ( PerModuleInfos . Num ( ) = = 1 )
. OnVerifyTextChanged ( this , & FRigModuleInstanceDetails : : OnVerifyShortNameChanged )
] ;
static const FText LongNameTooltip = LOCTEXT ( " LongNameTooltip " , " The long name represents a unique address within the rig but isn't used for the user interface. " ) ;
GeneralCategory . AddCustomRow ( FText : : FromString ( TEXT ( " Long Name " ) ) )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( FText : : FromString ( TEXT ( " Long Name " ) ) )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
. ToolTipText ( LOCTEXT ( " LongNameTooltip " , " The long name represents a unique address within the rig but isn't used for the user interface. " ) )
. ToolTipText ( LongNameTooltip )
. IsEnabled ( false )
]
. ValueContent ( )
[
SNew ( SInlineEditableTextBlock )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
. Text ( this , & FRigModuleInstanceDetails : : GetLongName )
. ToolTipText ( LongNameTooltip )
. IsEnabled ( false )
2023-11-09 05:26:12 -05:00
] ;
GeneralCategory . AddCustomRow ( FText : : FromString ( TEXT ( " RigClass " ) ) )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( FText : : FromString ( TEXT ( " RigClass " ) ) )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
. IsEnabled ( true )
]
. ValueContent ( )
[
SNew ( SInlineEditableTextBlock )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
. Text ( this , & FRigModuleInstanceDetails : : GetRigClassPath )
. IsEnabled ( true )
] ;
2024-05-24 04:04:04 -04:00
GeneralCategory . AddCustomRow ( FText : : FromString ( TEXT ( " Variant Tags " ) ) )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( FText : : FromString ( TEXT ( " Variant Tags " ) ) )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
. IsEnabled ( true )
]
. ValueContent ( )
[
SNew ( SRigVMVariantTagWidget )
. Orientation ( EOrientation : : Orient_Horizontal )
. CanAddTags ( false )
. EnableContextMenu ( false )
. OnGetTags_Lambda ( [ this ] ( ) - > TArray < FRigVMTag >
{
TArray < FRigVMTag > Tags ;
for ( int32 InfoIndex = 0 ; InfoIndex < PerModuleInfos . Num ( ) ; + + InfoIndex )
{
const FPerModuleInfo & ModuleInfo = PerModuleInfos [ InfoIndex ] ;
if ( ModuleInfo . Module . IsValid ( ) )
{
if ( const FRigModuleInstance * Module = ModuleInfo . GetModule ( ) )
{
if ( const UControlRigBlueprint * ModuleBlueprint = Cast < UControlRigBlueprint > ( Module - > GetRig ( ) - > GetClass ( ) - > ClassGeneratedBy ) )
{
if ( InfoIndex = = 0 )
{
Tags = ModuleBlueprint - > GetAssetVariant ( ) . Tags ;
}
else
{
const TArray < FRigVMTag > & OtherTags = ModuleBlueprint - > GetAssetVariant ( ) . Tags ;
bool bSameArray = Tags . Num ( ) = = OtherTags . Num ( ) ;
if ( bSameArray )
{
for ( const FRigVMTag & OtherTag : OtherTags )
{
if ( ! Tags . ContainsByPredicate ( [ OtherTag ] ( const FRigVMTag & Tag ) { return OtherTag . Name = = Tag . Name ; } ) )
{
return { } ;
}
}
}
else
{
return { } ;
}
}
}
}
}
}
return Tags ;
} )
] ;
2023-11-09 05:26:12 -05:00
}
IDetailCategoryBuilder & ConnectionsCategory = DetailBuilder . EditCategory ( TEXT ( " Connections " ) , LOCTEXT ( " Connections " , " Connections " ) ) ;
{
2024-01-22 05:49:06 -05:00
bool bDisplayConnectors = PerModuleInfos . Num ( ) > = 1 ;
if ( PerModuleInfos . Num ( ) > 1 )
2023-11-09 05:26:12 -05:00
{
2024-01-22 05:49:06 -05:00
UModularRig * ModularRig = PerModuleInfos [ 0 ] . GetModularRig ( ) ;
for ( FPerModuleInfo & Info : PerModuleInfos )
2024-01-19 04:34:33 -05:00
{
2024-01-22 05:49:06 -05:00
if ( Info . GetModularRig ( ) ! = ModularRig )
2024-01-19 04:34:33 -05:00
{
2024-01-22 05:49:06 -05:00
bDisplayConnectors = false ;
break ;
}
}
}
if ( bDisplayConnectors )
{
TArray < FRigModuleConnector > Connectors = GetConnectors ( ) ;
2024-08-21 09:49:15 -04:00
// sort connectors primary first, then secondary, then optional
Algo : : SortBy ( Connectors , [ ] ( const FRigModuleConnector & Connector ) - > int32
{
return Connector . IsPrimary ( ) ? 0 : ( Connector . IsOptional ( ) ? 2 : 1 ) ;
} ) ;
2024-01-22 05:49:06 -05:00
for ( const FRigModuleConnector & Connector : Connectors )
{
const FText Label = FText : : FromString ( Connector . Name ) ;
TSharedPtr < SVerticalBox > ButtonBox ;
TArray < FRigElementResolveResult > Matches ;
for ( int32 ModuleIndex = 0 ; ModuleIndex < PerModuleInfos . Num ( ) ; + + ModuleIndex )
{
const FPerModuleInfo & Info = PerModuleInfos [ ModuleIndex ] ;
if ( const FRigModuleInstance * Module = Info . GetModule ( ) )
2024-01-19 04:34:33 -05:00
{
2024-01-22 05:49:06 -05:00
if ( UModularRig * ModularRig = Info . GetModularRig ( ) )
2024-01-19 04:34:33 -05:00
{
2024-01-22 05:49:06 -05:00
if ( URigHierarchy * Hierarchy = ModularRig - > GetHierarchy ( ) )
{
FString ConnectorPath = FString : : Printf ( TEXT ( " %s:%s " ) , * Info . Path , * Connector . Name ) ;
FRigElementKey ConnectorKey ( * ConnectorPath , ERigElementType : : Connector ) ;
if ( FRigConnectorElement * ConnectorElement = Cast < FRigConnectorElement > ( Hierarchy - > Find ( ConnectorKey ) ) )
{
const UModularRigRuleManager * RuleManager = ModularRig - > GetHierarchy ( ) - > GetRuleManager ( ) ;
if ( ModuleIndex = = 0 )
{
Matches = RuleManager - > FindMatches ( ConnectorElement , Module , ModularRig - > GetElementKeyRedirector ( ) ) . GetMatches ( ) ;
}
else
{
const FModularRigResolveResult & ConnectorMatches = RuleManager - > FindMatches ( ConnectorElement , Module , ModularRig - > GetElementKeyRedirector ( ) ) ;
Matches . FilterByPredicate ( [ ConnectorMatches ] ( const FRigElementResolveResult & Match )
{
return ConnectorMatches . ContainsMatch ( Match . GetKey ( ) ) ;
} ) ;
}
}
}
2024-01-19 04:34:33 -05:00
}
}
}
2024-01-22 05:49:06 -05:00
FRigTreeDelegates TreeDelegates ;
TreeDelegates . OnGetHierarchy = FOnGetRigTreeHierarchy : : CreateLambda ( [ this ] ( )
2023-12-19 09:21:36 -05:00
{
2024-01-22 05:49:06 -05:00
return PerModuleInfos [ 0 ] . GetModularRig ( ) - > GetHierarchy ( ) ;
} ) ;
2024-09-16 12:15:02 -04:00
TArray < FRigElementKey > MatchingKeys ;
for ( const FRigElementResolveResult & SingleMatch : Matches )
2024-01-22 05:49:06 -05:00
{
2024-09-16 12:15:02 -04:00
MatchingKeys . Add ( SingleMatch . GetKey ( ) ) ;
}
TreeDelegates . OnRigTreeIsItemVisible = FOnRigTreeIsItemVisible : : CreateLambda ( [ MatchingKeys ] ( const FRigElementKey & InTarget )
{
return MatchingKeys . Contains ( InTarget ) ;
2024-01-22 05:49:06 -05:00
} ) ;
TreeDelegates . OnGetSelection . BindLambda ( [ this , Connector ] ( ) - > TArray < FRigElementKey >
{
FRigElementKey Target ;
FRigElementKeyRedirector Redirector = PerModuleInfos [ 0 ] . GetModularRig ( ) - > GetElementKeyRedirector ( ) ;
for ( int32 ModuleIndex = 0 ; ModuleIndex < PerModuleInfos . Num ( ) ; + + ModuleIndex )
{
FString ConnectorPath = FString : : Printf ( TEXT ( " %s:%s " ) , * PerModuleInfos [ ModuleIndex ] . Path , * Connector . Name ) ;
FRigElementKey ConnectorKey ( * ConnectorPath , ERigElementType : : Connector ) ;
if ( const FRigElementKey * Key = Redirector . FindExternalKey ( ConnectorKey ) )
{
if ( ModuleIndex = = 0 )
{
Target = * Key ;
}
else if ( Target ! = * Key )
{
Target . Name = * ControlRigModuleDetailsMultipleValues . ToString ( ) ;
return { Target } ;
}
}
else
{
Target . Name = * ControlRigModuleDetailsMultipleValues . ToString ( ) ;
return { Target } ;
}
}
return { } ;
} ) ;
TreeDelegates . OnSelectionChanged . BindSP ( this , & FRigModuleInstanceDetails : : OnConnectorTargetChanged , Connector ) ;
2023-12-19 09:21:36 -05:00
2024-08-21 09:49:15 -04:00
static const FSlateBrush * PrimaryBrush = FControlRigEditorStyle : : Get ( ) . GetBrush ( " ControlRig.ConnectorPrimary " ) ;
static const FSlateBrush * SecondaryBrush = FControlRigEditorStyle : : Get ( ) . GetBrush ( " ControlRig.ConnectorSecondary " ) ;
static const FSlateBrush * OptionalBrush = FControlRigEditorStyle : : Get ( ) . GetBrush ( " ControlRig.ConnectorOptional " ) ;
const FSlateBrush * IconBrush = Connector . IsPrimary ( ) ? PrimaryBrush : ( Connector . IsOptional ( ) ? OptionalBrush : SecondaryBrush ) ;
2024-09-16 12:15:02 -04:00
TSharedPtr < SSearchableRigHierarchyTreeView > & SearchableTreeView = ConnectionListBox . FindOrAdd ( Connector . Name ) ;
2023-12-19 09:21:36 -05:00
2024-01-22 05:49:06 -05:00
ConnectionsCategory . AddCustomRow ( Label )
. NameContent ( )
2023-12-19 09:21:36 -05:00
[
2024-08-21 09:49:15 -04:00
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( 0.f , 0.f , 4.f , 0.f )
. HAlign ( HAlign_Left )
. VAlign ( VAlign_Center )
[
SNew ( SImage )
. Image ( IconBrush )
. ColorAndOpacity ( FSlateColor : : UseForeground ( ) )
. DesiredSizeOverride ( FVector2D ( 16 , 16 ) )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( 0.f , 0.f , 0.f , 0.f )
. HAlign ( HAlign_Left )
. VAlign ( VAlign_Center )
[
SNew ( STextBlock )
. Text ( Label )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
. IsEnabled ( true )
]
2023-12-19 09:21:36 -05:00
]
2024-01-22 05:49:06 -05:00
. ValueContent ( )
2023-12-19 09:21:36 -05:00
[
2024-01-22 05:49:06 -05:00
SNew ( SHorizontalBox )
// Combo button
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( 0.f , 0.f , 0.f , 0.f )
[
SNew ( SComboButton )
. ContentPadding ( 3 )
. MenuPlacement ( MenuPlacement_BelowAnchor )
. OnComboBoxOpened ( this , & FRigModuleInstanceDetails : : PopulateConnectorTargetList , Connector . Name )
. ButtonContent ( )
[
// Wrap in configurable box to restrain height/width of menu
SNew ( SBox )
. MinDesiredWidth ( 150.0f )
[
SAssignNew ( ButtonBox , SVerticalBox )
]
]
. MenuContent ( )
[
SNew ( SBorder )
. Visibility ( EVisibility : : Visible )
. BorderImage ( FAppStyle : : GetBrush ( " Menu.Background " ) )
[
2024-09-16 12:15:02 -04:00
SAssignNew ( SearchableTreeView , SSearchableRigHierarchyTreeView )
2024-01-22 05:49:06 -05:00
. RigTreeDelegates ( TreeDelegates )
]
]
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
2023-12-19 11:01:10 -05:00
. VAlign ( VAlign_Center )
2024-01-22 05:49:06 -05:00
. Padding ( 0.f , 0.f , 0.f , 0.f )
2023-12-19 09:21:36 -05:00
[
2024-01-22 05:49:06 -05:00
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. VAlign ( VAlign_Center )
. AutoHeight ( )
2023-12-19 11:01:10 -05:00
[
2024-01-22 05:49:06 -05:00
// Reset button
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( 0.f , 0.f , 0.f , 0.f )
2023-12-19 11:01:10 -05:00
[
2024-01-22 05:49:06 -05:00
SAssignNew ( ResetConnectorButton . FindOrAdd ( Connector . Name ) , SButton )
. ButtonStyle ( FAppStyle : : Get ( ) , " NoBorder " )
. ButtonColorAndOpacity_Lambda ( [ this , Connector ] ( )
2023-12-19 11:01:10 -05:00
{
2024-01-22 05:49:06 -05:00
const TSharedPtr < SButton > & Button = ResetConnectorButton . FindRef ( Connector . Name ) ;
2023-12-19 11:01:10 -05:00
return Button . IsValid ( ) & & Button - > IsHovered ( )
2024-01-22 05:49:06 -05:00
? FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.8 ) )
: FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.4 ) ) ;
2023-12-19 11:01:10 -05:00
} )
2024-01-22 05:49:06 -05:00
. OnClicked_Lambda ( [ this , Connector ] ( )
2023-12-19 11:01:10 -05:00
{
2024-01-22 05:49:06 -05:00
for ( FPerModuleInfo & Info : PerModuleInfos )
2023-12-19 11:01:10 -05:00
{
2024-01-22 05:49:06 -05:00
FString ConnectorPath = FString : : Printf ( TEXT ( " %s:%s " ) , * Info . Path , * Connector . Name ) ;
FRigElementKey ConnectorKey ( * ConnectorPath , ERigElementType : : Connector ) ;
Info . GetBlueprint ( ) - > GetModularRigController ( ) - > DisconnectConnector ( ConnectorKey ) ;
2023-12-19 11:01:10 -05:00
}
2024-01-22 05:49:06 -05:00
return FReply : : Handled ( ) ;
2023-12-19 11:01:10 -05:00
} )
2024-01-22 05:49:06 -05:00
. ContentPadding ( 1.f )
. ToolTipText ( NSLOCTEXT ( " ControlRigModuleDetails " , " Reset_Connector " , " Reset Connector " ) )
[
SNew ( SImage )
. ColorAndOpacity_Lambda ( [ this , Connector ] ( )
2023-12-19 11:01:10 -05:00
{
2024-01-22 05:49:06 -05:00
const TSharedPtr < SButton > & Button = ResetConnectorButton . FindRef ( Connector . Name ) ;
return Button . IsValid ( ) & & Button - > IsHovered ( )
? FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.8 ) )
: FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.4 ) ) ;
} )
. Image ( FSlateIcon ( FAppStyle : : Get ( ) . GetStyleSetName ( ) , " PropertyWindow.DiffersFromDefault " ) . GetIcon ( ) )
]
]
// Use button
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( 0.f , 0.f , 0.f , 0.f )
2023-12-19 11:01:10 -05:00
[
2024-01-22 05:49:06 -05:00
SAssignNew ( UseSelectedButton . FindOrAdd ( Connector . Name ) , SButton )
. ButtonStyle ( FAppStyle : : Get ( ) , " NoBorder " )
. ButtonColorAndOpacity_Lambda ( [ this , Connector ] ( )
2023-12-19 11:01:10 -05:00
{
2024-01-22 05:49:06 -05:00
const TSharedPtr < SButton > & Button = UseSelectedButton . FindRef ( Connector . Name ) ;
2023-12-19 11:01:10 -05:00
return Button . IsValid ( ) & & Button - > IsHovered ( )
2024-01-22 05:49:06 -05:00
? FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.8 ) )
: FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.4 ) ) ;
2023-12-19 11:01:10 -05:00
} )
2024-01-22 05:49:06 -05:00
. OnClicked_Lambda ( [ this , Connector ] ( )
{
if ( UModularRig * ModularRig = PerModuleInfos [ 0 ] . GetModularRig ( ) )
{
const TArray < FRigElementKey > & Selected = ModularRig - > GetHierarchy ( ) - > GetSelectedKeys ( ) ;
if ( Selected . Num ( ) > 0 )
{
for ( FPerModuleInfo & Info : PerModuleInfos )
{
FString ConnectorPath = FString : : Printf ( TEXT ( " %s:%s " ) , * Info . Path , * Connector . Name ) ;
FRigElementKey ConnectorKey ( * ConnectorPath , ERigElementType : : Connector ) ;
Info . GetBlueprint ( ) - > GetModularRigController ( ) - > ConnectConnectorToElement ( ConnectorKey , Selected [ 0 ] , true , ModularRig - > GetModularRigSettings ( ) . bAutoResolve ) ;
}
}
}
return FReply : : Handled ( ) ;
} )
. ContentPadding ( 1.f )
. ToolTipText ( NSLOCTEXT ( " ControlRigModuleDetails " , " Use_Selected " , " Use Selected " ) )
[
SNew ( SImage )
. ColorAndOpacity_Lambda ( [ this , Connector ] ( )
{
const TSharedPtr < SButton > & Button = UseSelectedButton . FindRef ( Connector . Name ) ;
return Button . IsValid ( ) & & Button - > IsHovered ( )
? FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.8 ) )
: FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.4 ) ) ;
} )
. Image ( FAppStyle : : GetBrush ( " Icons.CircleArrowLeft " ) )
]
]
// Select in hierarchy button
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( 0.f , 0.f , 0.f , 0.f )
[
SAssignNew ( SelectElementButton . FindOrAdd ( Connector . Name ) , SButton )
. ButtonStyle ( FAppStyle : : Get ( ) , " NoBorder " )
. ButtonColorAndOpacity_Lambda ( [ this , Connector ] ( )
{
const TSharedPtr < SButton > & Button = SelectElementButton . FindRef ( Connector . Name ) ;
return Button . IsValid ( ) & & Button - > IsHovered ( )
? FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.8 ) )
: FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.4 ) ) ;
} )
. OnClicked_Lambda ( [ this , Connector ] ( )
{
if ( UModularRig * ModularRig = PerModuleInfos [ 0 ] . GetModularRig ( ) )
{
FString ConnectorPath = FString : : Printf ( TEXT ( " %s:%s " ) , * PerModuleInfos [ 0 ] . Path , * Connector . Name ) ;
FRigElementKey ConnectorKey ( * ConnectorPath , ERigElementType : : Connector ) ;
if ( const FRigElementKey * TargetKey = ModularRig - > GetElementKeyRedirector ( ) . FindExternalKey ( ConnectorKey ) )
{
ModularRig - > GetHierarchy ( ) - > GetController ( ) - > SelectElement ( * TargetKey , true , true ) ;
}
}
return FReply : : Handled ( ) ;
} )
. ContentPadding ( 1.f )
. ToolTipText ( NSLOCTEXT ( " ControlRigModuleDetails " , " Select_Element " , " Select Element " ) )
[
SNew ( SImage )
. ColorAndOpacity_Lambda ( [ this , Connector ] ( )
{
const TSharedPtr < SButton > & Button = SelectElementButton . FindRef ( Connector . Name ) ;
return Button . IsValid ( ) & & Button - > IsHovered ( )
? FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.8 ) )
: FSlateColor ( FLinearColor ( 1.f , 1.f , 1.f , 0.4 ) ) ;
} )
. Image ( FAppStyle : : GetBrush ( " Icons.Search " ) )
]
2023-12-19 11:01:10 -05:00
]
]
2023-12-19 09:21:36 -05:00
]
2024-01-22 05:49:06 -05:00
] ;
2023-12-19 09:21:36 -05:00
2024-01-22 05:49:06 -05:00
FRigElementKey CurrentTargetKey ;
if ( PerModuleInfos . Num ( ) > = 1 )
{
for ( int32 ModuleIndex = 0 ; ModuleIndex < PerModuleInfos . Num ( ) ; + + ModuleIndex )
{
FRigElementKey TargetKey ;
FString ConnectorPath = FString : : Printf ( TEXT ( " %s:%s " ) , * PerModuleInfos [ ModuleIndex ] . Path , * Connector . Name ) ;
FRigElementKey ConnectorKey ( * ConnectorPath , ERigElementType : : Connector ) ;
if ( const FRigElementKey * Key = PerModuleInfos [ ModuleIndex ] . GetModularRig ( ) - > GetElementKeyRedirector ( ) . FindExternalKey ( ConnectorKey ) )
{
TargetKey = * Key ;
}
if ( ModuleIndex = = 0 )
{
CurrentTargetKey = TargetKey ;
}
else
{
if ( CurrentTargetKey ! = TargetKey )
{
CurrentTargetKey . Name = * ControlRigModuleDetailsMultipleValues . ToString ( ) ;
}
}
}
}
TPair < const FSlateBrush * , FSlateColor > IconAndColor = SRigHierarchyItem : : GetBrushForElementType ( PerModuleInfos [ 0 ] . GetModularRig ( ) - > GetHierarchy ( ) , CurrentTargetKey ) ;
PopulateConnectorCurrentTarget ( ButtonBox , IconAndColor . Key , IconAndColor . Value , FText : : FromName ( CurrentTargetKey . Name ) ) ;
2023-12-19 09:21:36 -05:00
}
2023-11-09 05:26:12 -05:00
}
}
2023-12-07 06:01:49 -05:00
for ( const FName & OriginalCategoryName : OriginalCategoryNames )
2023-11-09 05:26:12 -05:00
{
2023-12-07 06:01:49 -05:00
IDetailCategoryBuilder & Category = DetailBuilder . EditCategory ( OriginalCategoryName ) ;
2023-11-09 05:26:12 -05:00
{
2023-12-07 06:01:49 -05:00
IPropertyAccessEditor & PropertyAccessEditor = IModularFeatures : : Get ( ) . GetModularFeature < IPropertyAccessEditor > ( " PropertyAccessEditor " ) ;
TArray < TSharedRef < IPropertyHandle > > DefaultProperties ;
Category . GetDefaultProperties ( DefaultProperties , true , true ) ;
for ( const TSharedRef < IPropertyHandle > & DefaultProperty : DefaultProperties )
2023-11-09 05:26:12 -05:00
{
2023-12-07 06:01:49 -05:00
const FProperty * Property = DefaultProperty - > GetProperty ( ) ;
if ( Property = = nullptr )
2023-11-21 12:05:15 -05:00
{
2023-12-07 06:01:49 -05:00
DetailBuilder . HideProperty ( DefaultProperty ) ;
continue ;
}
2023-11-21 12:05:15 -05:00
2023-12-07 06:01:49 -05:00
// skip advanced properties for now
const bool bAdvancedDisplay = Property - > HasAnyPropertyFlags ( CPF_AdvancedDisplay ) ;
if ( bAdvancedDisplay )
{
DetailBuilder . HideProperty ( DefaultProperty ) ;
continue ;
}
2023-11-21 12:05:15 -05:00
2023-12-07 06:01:49 -05:00
// skip non-public properties for now
const bool bIsPublic = Property - > HasAnyPropertyFlags ( CPF_Edit | CPF_EditConst ) ;
const bool bIsInstanceEditable = ! Property - > HasAnyPropertyFlags ( CPF_DisableEditOnInstance ) ;
if ( ! bIsPublic | | ! bIsInstanceEditable )
{
DetailBuilder . HideProperty ( DefaultProperty ) ;
continue ;
}
2023-11-21 12:05:15 -05:00
2023-12-07 06:01:49 -05:00
const FSimpleDelegate OnValueChangedDelegate = FSimpleDelegate : : CreateSP ( this , & FRigModuleInstanceDetails : : OnConfigValueChanged , Property - > GetFName ( ) ) ;
DefaultProperty - > SetOnPropertyValueChanged ( OnValueChangedDelegate ) ;
DefaultProperty - > SetOnChildPropertyValueChanged ( OnValueChangedDelegate ) ;
FPropertyBindingWidgetArgs BindingArgs ;
BindingArgs . Property = ( FProperty * ) Property ;
BindingArgs . CurrentBindingText = TAttribute < FText > : : CreateLambda ( [ this , Property ] ( )
{
return GetBindingText ( Property ) ;
} ) ;
BindingArgs . CurrentBindingImage = TAttribute < const FSlateBrush * > : : CreateLambda ( [ this , Property ] ( )
{
return GetBindingImage ( Property ) ;
} ) ;
BindingArgs . CurrentBindingColor = TAttribute < FLinearColor > : : CreateLambda ( [ this , Property ] ( )
{
return GetBindingColor ( Property ) ;
} ) ;
2024-02-14 07:07:33 -05:00
BindingArgs . OnCanBindPropertyWithBindingChain . BindLambda ( [ ] ( const FProperty * InProperty , TConstArrayView < FBindingChainElement > InBindingChain ) - > bool { return true ; } ) ;
2023-12-07 06:01:49 -05:00
BindingArgs . OnCanBindToClass . BindLambda ( [ ] ( UClass * InClass ) - > bool { return false ; } ) ;
BindingArgs . OnCanRemoveBinding . BindRaw ( this , & FRigModuleInstanceDetails : : CanRemoveBinding ) ;
BindingArgs . OnRemoveBinding . BindSP ( this , & FRigModuleInstanceDetails : : HandleRemoveBinding ) ;
BindingArgs . bGeneratePureBindings = true ;
BindingArgs . bAllowNewBindings = true ;
BindingArgs . bAllowArrayElementBindings = false ;
BindingArgs . bAllowStructMemberBindings = false ;
BindingArgs . bAllowUObjectFunctions = false ;
BindingArgs . MenuExtender = MakeShareable ( new FExtender ) ;
BindingArgs . MenuExtender - > AddMenuExtension (
" Properties " ,
EExtensionHook : : After ,
nullptr ,
FMenuExtensionDelegate : : CreateSPLambda ( this , [ this , Property ] ( FMenuBuilder & MenuBuilder )
{
FillBindingMenu ( MenuBuilder , Property ) ;
} )
) ;
2024-02-13 17:12:39 -05:00
TSharedPtr < SWidget > ValueWidget = DefaultProperty - > CreatePropertyValueWidgetWithCustomization ( DetailBuilder . GetDetailsView ( ) ) ;
2023-12-07 06:01:49 -05:00
2024-02-13 17:12:39 -05:00
const bool bShowChildren = true ;
Category . AddProperty ( DefaultProperty ) . CustomWidget ( bShowChildren )
2023-12-07 06:01:49 -05:00
. NameContent ( )
[
DefaultProperty - > CreatePropertyNameWidget ( )
]
. ValueContent ( )
[
2024-02-13 17:12:39 -05:00
ValueWidget ? ValueWidget . ToSharedRef ( ) : SNullWidget : : NullWidget
2023-12-07 06:01:49 -05:00
// todo: if the property is bound / or partially bound
// mark the property value widget as disabled / read only.
]
2024-02-13 17:12:39 -05:00
2023-12-07 06:01:49 -05:00
. ExtensionContent ( )
[
PropertyAccessEditor . MakePropertyBindingWidget ( nullptr , BindingArgs )
] ;
}
2023-11-09 05:26:12 -05:00
}
}
}
FText FRigModuleInstanceDetails : : GetName ( ) const
{
2023-12-07 05:11:51 -05:00
const FRigModuleInstance * FirstModule = PerModuleInfos [ 0 ] . GetModule ( ) ;
if ( FirstModule = = nullptr )
{
return FText ( ) ;
}
const FName FirstValue = FirstModule - > Name ;
2023-11-09 05:26:12 -05:00
if ( PerModuleInfos . Num ( ) > 1 )
{
bool bSame = true ;
for ( int32 i = 1 ; i < PerModuleInfos . Num ( ) ; + + i )
{
2023-12-07 05:11:51 -05:00
if ( const FRigModuleInstance * Module = PerModuleInfos [ i ] . GetModule ( ) )
2023-11-09 05:26:12 -05:00
{
2024-09-18 07:58:28 -04:00
if ( ! Module - > Name . IsEqual ( FirstValue , ENameCase : : IgnoreCase ) )
2023-12-07 05:11:51 -05:00
{
bSame = false ;
break ;
}
2023-11-09 05:26:12 -05:00
}
}
if ( ! bSame )
{
return ControlRigModuleDetailsMultipleValues ;
}
}
2023-12-07 05:11:51 -05:00
return FText : : FromName ( FirstValue ) ;
2023-11-09 05:26:12 -05:00
}
2023-12-07 05:11:51 -05:00
void FRigModuleInstanceDetails : : SetName ( const FText & InValue , ETextCommit : : Type InCommitType , const TSharedRef < IPropertyUtilities > PropertyUtilities )
2023-11-09 05:26:12 -05:00
{
2023-12-07 05:11:51 -05:00
if ( InValue . IsEmpty ( ) )
{
return ;
}
2024-01-22 05:49:06 -05:00
for ( FPerModuleInfo & Info : PerModuleInfos )
2023-12-07 05:11:51 -05:00
{
2024-01-22 05:49:06 -05:00
if ( const FRigModuleInstance * ModuleInstance = Info . GetModule ( ) )
2023-12-07 05:11:51 -05:00
{
2024-01-22 05:49:06 -05:00
if ( UControlRigBlueprint * Blueprint = Info . GetBlueprint ( ) )
{
UModularRigController * Controller = Blueprint - > GetModularRigController ( ) ;
const FString OldPath = ModuleInstance - > GetPath ( ) ;
( void ) Controller - > RenameModule ( OldPath , * InValue . ToString ( ) , true ) ;
}
2023-12-07 05:11:51 -05:00
}
}
}
bool FRigModuleInstanceDetails : : OnVerifyNameChanged ( const FText & InText , FText & OutErrorMessage )
{
if ( InText . IsEmpty ( ) )
{
static const FText EmptyNameIsNotAllowed = LOCTEXT ( " EmptyNameIsNotAllowed " , " Empty name is not allowed. " ) ;
OutErrorMessage = EmptyNameIsNotAllowed ;
return false ;
}
2024-01-22 05:49:06 -05:00
for ( FPerModuleInfo & Info : PerModuleInfos )
2023-12-07 05:11:51 -05:00
{
2024-01-22 05:49:06 -05:00
if ( const FRigModuleInstance * ModuleInstance = Info . GetModule ( ) )
2023-12-07 05:11:51 -05:00
{
2024-01-22 05:49:06 -05:00
if ( UControlRigBlueprint * Blueprint = Info . GetBlueprint ( ) )
{
UModularRigController * Controller = Blueprint - > GetModularRigController ( ) ;
if ( ! Controller - > CanRenameModule ( ModuleInstance - > GetPath ( ) , * InText . ToString ( ) , OutErrorMessage ) )
{
return false ;
}
}
2023-12-07 05:11:51 -05:00
}
}
return true ;
}
FText FRigModuleInstanceDetails : : GetShortName ( ) const
{
const FRigModuleInstance * FirstModule = PerModuleInfos [ 0 ] . GetModule ( ) ;
if ( FirstModule = = nullptr )
{
return FText ( ) ;
}
const FString FirstValue = FirstModule - > GetShortName ( ) ;
2023-11-09 05:26:12 -05:00
if ( PerModuleInfos . Num ( ) > 1 )
{
bool bSame = true ;
for ( int32 i = 1 ; i < PerModuleInfos . Num ( ) ; + + i )
{
2023-12-07 05:11:51 -05:00
if ( const FRigModuleInstance * Module = PerModuleInfos [ i ] . GetModule ( ) )
2023-11-09 05:26:12 -05:00
{
2024-09-18 07:58:28 -04:00
if ( ! Module - > GetShortName ( ) . Equals ( FirstValue , ESearchCase : : IgnoreCase ) )
2023-12-07 05:11:51 -05:00
{
bSame = false ;
break ;
}
2023-11-09 05:26:12 -05:00
}
}
if ( ! bSame )
{
return ControlRigModuleDetailsMultipleValues ;
}
}
2023-12-07 05:11:51 -05:00
return FText : : FromString ( FirstValue ) ;
}
void FRigModuleInstanceDetails : : SetShortName ( const FText & InValue , ETextCommit : : Type InCommitType , const TSharedRef < IPropertyUtilities > PropertyUtilities )
{
if ( InValue . IsEmpty ( ) )
{
return ;
}
check ( PerModuleInfos . Num ( ) = = 1 ) ;
const FPerModuleInfo & Info = PerModuleInfos [ 0 ] ;
if ( const FRigModuleInstance * ModuleInstance = Info . GetModule ( ) )
{
if ( UControlRigBlueprint * Blueprint = Info . GetBlueprint ( ) )
{
UModularRigController * Controller = Blueprint - > GetModularRigController ( ) ;
Controller - > SetModuleShortName ( ModuleInstance - > GetPath ( ) , * InValue . ToString ( ) , true ) ;
}
}
}
bool FRigModuleInstanceDetails : : OnVerifyShortNameChanged ( const FText & InText , FText & OutErrorMessage )
{
check ( PerModuleInfos . Num ( ) = = 1 ) ;
if ( InText . IsEmpty ( ) )
{
return true ;
}
const FPerModuleInfo & Info = PerModuleInfos [ 0 ] ;
if ( const FRigModuleInstance * ModuleInstance = Info . GetModule ( ) )
{
if ( UControlRigBlueprint * Blueprint = Info . GetBlueprint ( ) )
{
UModularRigController * Controller = Blueprint - > GetModularRigController ( ) ;
return Controller - > CanSetModuleShortName ( ModuleInstance - > GetPath ( ) , * InText . ToString ( ) , OutErrorMessage ) ;
}
}
return false ;
}
FText FRigModuleInstanceDetails : : GetLongName ( ) const
{
const FRigModuleInstance * FirstModule = PerModuleInfos [ 0 ] . GetModule ( ) ;
if ( FirstModule = = nullptr )
{
return FText ( ) ;
}
const FString FirstValue = FirstModule - > GetLongName ( ) ;
if ( PerModuleInfos . Num ( ) > 1 )
{
bool bSame = true ;
for ( int32 i = 1 ; i < PerModuleInfos . Num ( ) ; + + i )
{
if ( const FRigModuleInstance * Module = PerModuleInfos [ i ] . GetModule ( ) )
{
2024-09-18 07:58:28 -04:00
if ( ! Module - > GetLongName ( ) . Equals ( FirstValue , ESearchCase : : IgnoreCase ) )
2023-12-07 05:11:51 -05:00
{
bSame = false ;
break ;
}
}
}
if ( ! bSame )
{
return ControlRigModuleDetailsMultipleValues ;
}
}
return FText : : FromString ( FirstValue ) ;
}
FText FRigModuleInstanceDetails : : GetRigClassPath ( ) const
{
if ( PerModuleInfos . Num ( ) > 1 )
{
if ( const FRigModuleInstance * FirstModule = PerModuleInfos [ 0 ] . GetModule ( ) )
{
bool bSame = true ;
for ( int32 i = 1 ; i < PerModuleInfos . Num ( ) ; + + i )
{
if ( const FRigModuleInstance * Module = PerModuleInfos [ i ] . GetModule ( ) )
{
2024-01-22 05:49:06 -05:00
if ( Module - > GetRig ( ) - > GetClass ( ) ! = FirstModule - > GetRig ( ) - > GetClass ( ) )
2023-12-07 05:11:51 -05:00
{
bSame = false ;
break ;
}
}
}
if ( ! bSame )
{
return ControlRigModuleDetailsMultipleValues ;
}
}
}
2023-11-09 05:26:12 -05:00
2023-11-29 03:23:17 -05:00
if ( const FRigModuleInstance * Module = PerModuleInfos [ 0 ] . GetModule ( ) )
2023-11-09 05:26:12 -05:00
{
2023-11-29 03:23:17 -05:00
if ( const UControlRig * ModuleRig = Module - > GetRig ( ) )
2023-11-09 05:26:12 -05:00
{
2023-11-29 03:23:17 -05:00
return FText : : FromString ( ModuleRig - > GetClass ( ) - > GetClassPathName ( ) . ToString ( ) ) ;
2023-11-09 05:26:12 -05:00
}
}
return FText ( ) ;
}
2023-11-15 07:37:23 -05:00
TArray < FRigModuleConnector > FRigModuleInstanceDetails : : GetConnectors ( ) const
{
if ( PerModuleInfos . Num ( ) > 1 )
{
2024-01-22 05:49:06 -05:00
TArray < FRigModuleConnector > CommonConnectors ;
if ( const FRigModuleInstance * Module = PerModuleInfos [ 0 ] . GetModule ( ) )
{
if ( const UControlRig * ModuleRig = Module - > GetRig ( ) )
{
CommonConnectors = ModuleRig - > GetRigModuleSettings ( ) . ExposedConnectors ;
}
}
for ( int32 ModuleIndex = 1 ; ModuleIndex < PerModuleInfos . Num ( ) ; + + ModuleIndex )
{
if ( const FRigModuleInstance * Module = PerModuleInfos [ ModuleIndex ] . GetModule ( ) )
{
if ( const UControlRig * ModuleRig = Module - > GetRig ( ) )
{
const TArray < FRigModuleConnector > & ModuleConnectors = ModuleRig - > GetRigModuleSettings ( ) . ExposedConnectors ;
CommonConnectors = CommonConnectors . FilterByPredicate ( [ ModuleConnectors ] ( const FRigModuleConnector & Connector )
{
return ModuleConnectors . Contains ( Connector ) ;
} ) ;
}
}
}
return CommonConnectors ;
2023-11-15 07:37:23 -05:00
}
2023-11-29 03:23:17 -05:00
if ( const FRigModuleInstance * Module = PerModuleInfos [ 0 ] . GetModule ( ) )
2023-11-15 07:37:23 -05:00
{
2023-11-29 03:23:17 -05:00
if ( const UControlRig * ModuleRig = Module - > GetRig ( ) )
2023-11-15 07:37:23 -05:00
{
2023-11-29 03:23:17 -05:00
return ModuleRig - > GetRigModuleSettings ( ) . ExposedConnectors ;
2023-11-15 07:37:23 -05:00
}
}
return TArray < FRigModuleConnector > ( ) ;
}
2023-11-09 05:26:12 -05:00
FRigElementKeyRedirector FRigModuleInstanceDetails : : GetConnections ( ) const
{
if ( PerModuleInfos . Num ( ) > 1 )
{
return FRigElementKeyRedirector ( ) ;
}
2023-11-29 03:23:17 -05:00
if ( const FRigModuleInstance * Module = PerModuleInfos [ 0 ] . GetModule ( ) )
2023-11-09 05:26:12 -05:00
{
2023-11-29 03:23:17 -05:00
if ( UControlRig * ModuleRig = Module - > GetRig ( ) )
2023-11-09 05:26:12 -05:00
{
2023-11-29 03:23:17 -05:00
return ModuleRig - > GetElementKeyRedirector ( ) ;
2023-11-09 05:26:12 -05:00
}
}
return FRigElementKeyRedirector ( ) ;
}
2024-01-22 05:49:06 -05:00
void FRigModuleInstanceDetails : : PopulateConnectorTargetList ( const FString InConnectorName )
2023-12-19 09:21:36 -05:00
{
2024-01-22 05:49:06 -05:00
ConnectionListBox . FindRef ( InConnectorName ) - > GetTreeView ( ) - > RefreshTreeView ( true ) ;
2023-12-19 09:21:36 -05:00
}
2024-01-22 05:49:06 -05:00
void FRigModuleInstanceDetails : : PopulateConnectorCurrentTarget ( TSharedPtr < SVerticalBox > InListBox , const FSlateBrush * InBrush , const FSlateColor & InColor , const FText & InTitle )
2023-12-19 09:21:36 -05:00
{
static const FSlateBrush * RoundedBoxBrush = FControlRigEditorStyle : : Get ( ) . GetBrush ( TEXT ( " ControlRig.SpacePicker.RoundedRect " ) ) ;
2024-01-22 05:49:06 -05:00
2023-12-19 09:21:36 -05:00
TSharedPtr < SHorizontalBox > RowBox , ButtonBox ;
InListBox - > AddSlot ( )
. AutoHeight ( )
. VAlign ( VAlign_Top )
. HAlign ( HAlign_Fill )
. Padding ( 4.0 , 0.0 , 4.0 , 0.0 )
[
SNew ( SButton )
. ButtonStyle ( FAppStyle : : Get ( ) , " SimpleButton " )
. ContentPadding ( FMargin ( 0.0 ) )
[
SAssignNew ( RowBox , SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
. HAlign ( HAlign_Fill )
. Padding ( 0 )
[
SNew ( SBorder )
. Padding ( FMargin ( 5.0 , 2.0 , 5.0 , 2.0 ) )
. BorderImage ( RoundedBoxBrush )
. BorderBackgroundColor ( FSlateColor ( FLinearColor : : Transparent ) )
. Content ( )
[
SAssignNew ( ButtonBox , SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
. HAlign ( HAlign_Left )
. Padding ( FMargin ( 0.f , 0.f , 3.f , 0.f ) )
[
SNew ( SImage )
. Image ( InBrush )
. ColorAndOpacity ( InColor )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
. HAlign ( HAlign_Left )
. Padding ( 0 )
[
SNew ( STextBlock )
. Text ( InTitle )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
]
+ SHorizontalBox : : Slot ( )
. FillWidth ( 1.f )
[
SNew ( SSpacer )
]
]
]
]
] ;
}
2023-11-09 05:26:12 -05:00
void FRigModuleInstanceDetails : : OnConfigValueChanged ( const FName InVariableName )
{
2024-01-22 05:49:06 -05:00
TMap < FString , FString > ModuleValues ;
2023-11-21 12:05:15 -05:00
for ( const FPerModuleInfo & Info : PerModuleInfos )
2023-11-09 05:26:12 -05:00
{
2023-11-21 12:05:15 -05:00
if ( const FRigModuleInstance * ModuleInstance = Info . GetModule ( ) )
2023-11-09 05:26:12 -05:00
{
2023-11-29 03:23:17 -05:00
if ( const UControlRig * ModuleRig = ModuleInstance - > GetRig ( ) )
2023-11-09 05:26:12 -05:00
{
2023-11-29 03:23:17 -05:00
FString ValueStr = ModuleRig - > GetVariableAsString ( InVariableName ) ;
2024-01-22 05:49:06 -05:00
ModuleValues . Add ( ModuleInstance - > GetPath ( ) , ValueStr ) ;
2023-11-09 05:26:12 -05:00
}
}
}
2024-01-22 05:49:06 -05:00
for ( const TPair < FString , FString > & Value : ModuleValues )
{
if ( UControlRigBlueprint * Blueprint = PerModuleInfos [ 0 ] . GetBlueprint ( ) )
{
UModularRigController * Controller = Blueprint - > GetModularRigController ( ) ;
Controller - > SetConfigValueInModule ( Value . Key , InVariableName , Value . Value ) ;
}
}
2023-11-09 05:26:12 -05:00
}
2024-01-22 05:49:06 -05:00
void FRigModuleInstanceDetails : : OnConnectorTargetChanged ( TSharedPtr < FRigTreeElement > Selection , ESelectInfo : : Type SelectInfo , const FRigModuleConnector InConnector )
2023-12-20 09:38:04 -05:00
{
if ( SelectInfo = = ESelectInfo : : OnNavigation )
{
return ;
}
FScopedTransaction Transaction ( LOCTEXT ( " ModuleHierarchyResolveConnector " , " Resolve Connector " ) ) ;
2024-01-22 05:49:06 -05:00
for ( FPerModuleInfo & Info : PerModuleInfos )
2023-12-20 09:38:04 -05:00
{
2024-01-22 05:49:06 -05:00
if ( UModularRigController * Controller = Info . GetBlueprint ( ) - > GetModularRigController ( ) )
2023-12-20 09:38:04 -05:00
{
2024-01-22 05:49:06 -05:00
FString ConnectorPath = FString : : Printf ( TEXT ( " %s:%s " ) , * Info . Path , * InConnector . Name ) ;
FRigElementKey ConnectorKey ( * ConnectorPath , ERigElementType : : Connector ) ;
if ( Selection . IsValid ( ) )
{
const FModularRigSettings & Settings = Info . GetModularRig ( ) - > GetModularRigSettings ( ) ;
Controller - > ConnectConnectorToElement ( ConnectorKey , Selection - > Key , true , Settings . bAutoResolve ) ;
}
else
{
Controller - > DisconnectConnector ( ConnectorKey ) ;
}
2023-12-20 09:38:04 -05:00
}
}
}
2023-11-09 05:26:12 -05:00
const FRigModuleInstanceDetails : : FPerModuleInfo & FRigModuleInstanceDetails : : FindModule ( const FString & InPath ) const
{
const FPerModuleInfo * Info = FindModuleByPredicate ( [ InPath ] ( const FPerModuleInfo & Info )
{
2023-12-07 05:11:51 -05:00
if ( const FRigModuleInstance * Module = Info . GetModule ( ) )
{
return Module - > GetPath ( ) = = InPath ;
}
return false ;
2023-11-09 05:26:12 -05:00
} ) ;
if ( Info )
{
return * Info ;
}
static const FPerModuleInfo EmptyInfo ;
return EmptyInfo ;
}
const FRigModuleInstanceDetails : : FPerModuleInfo * FRigModuleInstanceDetails : : FindModuleByPredicate ( const TFunction < bool ( const FPerModuleInfo & ) > & InPredicate ) const
{
return PerModuleInfos . FindByPredicate ( InPredicate ) ;
}
bool FRigModuleInstanceDetails : : ContainsModuleByPredicate ( const TFunction < bool ( const FPerModuleInfo & ) > & InPredicate ) const
{
return PerModuleInfos . ContainsByPredicate ( InPredicate ) ;
}
void FRigModuleInstanceDetails : : RegisterSectionMappings ( FPropertyEditorModule & PropertyEditorModule , UClass * InClass )
{
TSharedRef < FPropertySection > MetadataSection = PropertyEditorModule . FindOrCreateSection ( InClass - > GetFName ( ) , " Metadata " , LOCTEXT ( " Metadata " , " Metadata " ) ) ;
MetadataSection - > AddCategory ( " Metadata " ) ;
}
2023-11-21 12:05:15 -05:00
FText FRigModuleInstanceDetails : : GetBindingText ( const FProperty * InProperty ) const
{
const FName VariableName = InProperty - > GetFName ( ) ;
FText FirstValue ;
2024-01-22 05:49:06 -05:00
for ( int32 ModuleIndex = 0 ; ModuleIndex < PerModuleInfos . Num ( ) ; + + ModuleIndex )
2023-11-21 12:05:15 -05:00
{
2024-01-22 05:49:06 -05:00
if ( const FRigModuleReference * ModuleReference = PerModuleInfos [ ModuleIndex ] . GetReference ( ) )
2023-11-21 12:05:15 -05:00
{
if ( ModuleReference - > Bindings . Contains ( VariableName ) )
{
const FText BindingText = FText : : FromString ( ModuleReference - > Bindings . FindChecked ( VariableName ) ) ;
2024-01-22 05:49:06 -05:00
if ( ModuleIndex = = 0 )
2023-11-21 12:05:15 -05:00
{
FirstValue = BindingText ;
}
else if ( ! FirstValue . EqualTo ( BindingText ) )
{
return ControlRigModuleDetailsMultipleValues ;
}
}
}
}
return FirstValue ;
}
const FSlateBrush * FRigModuleInstanceDetails : : GetBindingImage ( const FProperty * InProperty ) const
{
2024-03-28 07:07:53 -04:00
static const FLazyName TypeIcon ( TEXT ( " Kismet.VariableList.TypeIcon " ) ) ;
static const FLazyName ArrayTypeIcon ( TEXT ( " Kismet.VariableList.ArrayTypeIcon " ) ) ;
2023-11-21 12:05:15 -05:00
if ( CastField < FArrayProperty > ( InProperty ) )
{
return FAppStyle : : GetBrush ( ArrayTypeIcon ) ;
}
return FAppStyle : : GetBrush ( TypeIcon ) ;
}
FLinearColor FRigModuleInstanceDetails : : GetBindingColor ( const FProperty * InProperty ) const
{
if ( InProperty )
{
FEdGraphPinType PinType ;
const UEdGraphSchema_K2 * Schema_K2 = GetDefault < UEdGraphSchema_K2 > ( ) ;
if ( Schema_K2 - > ConvertPropertyToPinType ( InProperty , PinType ) )
{
const URigVMEdGraphSchema * Schema = GetDefault < URigVMEdGraphSchema > ( ) ;
return Schema - > GetPinTypeColor ( PinType ) ;
}
}
return FLinearColor : : White ;
}
void FRigModuleInstanceDetails : : FillBindingMenu ( FMenuBuilder & MenuBuilder , const FProperty * InProperty ) const
{
if ( PerModuleInfos . IsEmpty ( ) )
{
return ;
}
UControlRigBlueprint * Blueprint = PerModuleInfos [ 0 ] . GetBlueprint ( ) ;
UModularRigController * Controller = Blueprint - > GetModularRigController ( ) ;
TArray < FString > CombinedBindings ;
for ( int32 Index = 0 ; Index < PerModuleInfos . Num ( ) ; Index + + )
{
const FPerModuleInfo & Info = PerModuleInfos [ Index ] ;
const TArray < FString > Bindings = Controller - > GetPossibleBindings ( Info . GetPath ( ) , InProperty - > GetFName ( ) ) ;
if ( Index = = 0 )
{
CombinedBindings = Bindings ;
}
else
{
// reduce the set of bindings to the overall possible bindings
CombinedBindings . RemoveAll ( [ Bindings ] ( const FString & Binding )
{
return ! Bindings . Contains ( Binding ) ;
} ) ;
}
}
2023-11-23 09:49:50 -05:00
if ( CombinedBindings . IsEmpty ( ) )
{
MenuBuilder . AddMenuEntry (
FUIAction ( FExecuteAction ( ) ) ,
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
. Padding ( 0.0f )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " NoBindingAvailable " , " No bindings available for this property. " ) )
. ColorAndOpacity ( FLinearColor : : White )
]
) ;
return ;
}
2023-11-21 12:05:15 -05:00
// sort lexically
CombinedBindings . Sort ( ) ;
// create a map of all of the variables per menu prefix (the module path the variables belong to)
struct FPerMenuData
{
FString Name ;
FString ParentMenuPath ;
TArray < FString > SubMenuPaths ;
TArray < FString > Variables ;
static void SetupMenu (
TSharedRef < FRigModuleInstanceDetails const > ThisDetails ,
const FProperty * InProperty ,
FMenuBuilder & InMenuBuilder ,
const FString & InMenuPath ,
TSharedRef < TMap < FString , FPerMenuData > > PerMenuData )
{
FPerMenuData & Data = PerMenuData - > FindChecked ( ( InMenuPath ) ) ;
Data . SubMenuPaths . Sort ( ) ;
Data . Variables . Sort ( ) ;
for ( const FString & VariablePath : Data . Variables )
{
FString VariableName = VariablePath ;
2024-01-23 06:29:09 -05:00
( void ) URigHierarchy : : SplitNameSpace ( VariablePath , nullptr , & VariableName ) ;
2023-11-21 12:05:15 -05:00
InMenuBuilder . AddMenuEntry (
FUIAction ( FExecuteAction : : CreateLambda ( [ ThisDetails , InProperty , VariablePath ] ( )
{
ThisDetails - > HandleChangeBinding ( InProperty , VariablePath ) ;
} ) ) ,
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
. Padding ( 1.0f , 0.0f )
[
SNew ( SImage )
. Image ( ThisDetails - > GetBindingImage ( InProperty ) )
. ColorAndOpacity ( ThisDetails - > GetBindingColor ( InProperty ) )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
. Padding ( 4.0f , 0.0f )
[
SNew ( STextBlock )
. Text ( FText : : FromString ( VariableName ) )
. ColorAndOpacity ( FLinearColor : : White )
]
) ;
}
for ( const FString & SubMenuPath : Data . SubMenuPaths )
{
const FPerMenuData & SubMenuData = PerMenuData - > FindChecked ( SubMenuPath ) ;
const FText Label = FText : : FromString ( SubMenuData . Name ) ;
static const FText TooltipFormat = LOCTEXT ( " BindingMenuTooltipFormat " , " Access to all variables of the {0} module " ) ;
const FText Tooltip = FText : : Format ( TooltipFormat , Label ) ;
InMenuBuilder . AddSubMenu ( Label , Tooltip , FNewMenuDelegate : : CreateLambda ( [ ThisDetails , InProperty , SubMenuPath , PerMenuData ] ( FMenuBuilder & SubMenuBuilder )
{
SetupMenu ( ThisDetails , InProperty , SubMenuBuilder , SubMenuPath , PerMenuData ) ;
} ) ) ;
}
}
} ;
// define the root menu
const TSharedRef < TMap < FString , FPerMenuData > > PerMenuData = MakeShared < TMap < FString , FPerMenuData > > ( ) ;
PerMenuData - > FindOrAdd ( FString ( ) ) ;
// make sure all levels of the menu are known and we have the variables available
for ( const FString & BindingPath : CombinedBindings )
{
FString MenuPath ;
2024-01-23 06:29:09 -05:00
( void ) URigHierarchy : : SplitNameSpace ( BindingPath , & MenuPath , nullptr ) ;
2023-11-21 12:05:15 -05:00
FString PreviousMenuPath = MenuPath ;
FString ParentMenuPath = MenuPath , RemainingPath ;
2024-01-23 06:29:09 -05:00
while ( URigHierarchy : : SplitNameSpace ( ParentMenuPath , & ParentMenuPath , & RemainingPath ) )
2023-11-21 12:05:15 -05:00
{
// scope since the map may change at the end of this block
{
FPerMenuData & Data = PerMenuData - > FindOrAdd ( MenuPath ) ;
if ( Data . Name . IsEmpty ( ) )
{
Data . Name = RemainingPath ;
}
}
PerMenuData - > FindOrAdd ( ParentMenuPath ) . SubMenuPaths . AddUnique ( PreviousMenuPath ) ;
PerMenuData - > FindOrAdd ( PreviousMenuPath ) . ParentMenuPath = ParentMenuPath ;
PerMenuData - > FindOrAdd ( PreviousMenuPath ) . Name = RemainingPath ;
if ( ! ParentMenuPath . Contains ( UModularRig : : NamespaceSeparator ) )
{
PerMenuData - > FindOrAdd ( FString ( ) ) . SubMenuPaths . AddUnique ( ParentMenuPath ) ;
PerMenuData - > FindOrAdd ( ParentMenuPath ) . Name = ParentMenuPath ;
}
PreviousMenuPath = ParentMenuPath ;
}
FPerMenuData & Data = PerMenuData - > FindOrAdd ( MenuPath ) ;
if ( Data . Name . IsEmpty ( ) )
{
Data . Name = MenuPath ;
}
Data . Variables . Add ( BindingPath ) ;
if ( ! MenuPath . IsEmpty ( ) )
{
PerMenuData - > FindChecked ( Data . ParentMenuPath ) . SubMenuPaths . AddUnique ( MenuPath ) ;
}
}
// build the menu
FPerMenuData : : SetupMenu ( SharedThis ( this ) , InProperty , MenuBuilder , FString ( ) , PerMenuData ) ;
}
bool FRigModuleInstanceDetails : : CanRemoveBinding ( FName InPropertyName ) const
{
// offer the "removing binding" button if any of the selected module instances
// has a binding for the given variable
for ( const FPerModuleInfo & Info : PerModuleInfos )
{
if ( const FRigModuleInstance * ModuleInstance = Info . GetModule ( ) )
{
if ( ModuleInstance - > VariableBindings . Contains ( InPropertyName ) )
{
return true ;
}
}
}
return false ;
}
void FRigModuleInstanceDetails : : HandleRemoveBinding ( FName InPropertyName ) const
{
2023-11-23 03:48:48 -05:00
FScopedTransaction Transaction ( LOCTEXT ( " RemoveModuleVariableTransaction " , " Remove Binding " ) ) ;
2023-11-21 12:05:15 -05:00
for ( const FPerModuleInfo & Info : PerModuleInfos )
{
if ( UControlRigBlueprint * Blueprint = Info . GetBlueprint ( ) )
{
if ( const FRigModuleInstance * ModuleInstance = Info . GetModule ( ) )
{
UModularRigController * Controller = Blueprint - > GetModularRigController ( ) ;
Controller - > UnBindModuleVariable ( ModuleInstance - > GetPath ( ) , InPropertyName ) ;
}
}
}
}
void FRigModuleInstanceDetails : : HandleChangeBinding ( const FProperty * InProperty , const FString & InNewVariablePath ) const
{
FScopedTransaction Transaction ( LOCTEXT ( " BindModuleVariableTransaction " , " Bind Module Variable " ) ) ;
for ( const FPerModuleInfo & Info : PerModuleInfos )
{
if ( UControlRigBlueprint * Blueprint = Info . GetBlueprint ( ) )
{
if ( const FRigModuleInstance * ModuleInstance = Info . GetModule ( ) )
{
UModularRigController * Controller = Blueprint - > GetModularRigController ( ) ;
Controller - > BindModuleVariable ( ModuleInstance - > GetPath ( ) , InProperty - > GetFName ( ) , InNewVariablePath ) ;
}
}
}
}
2023-11-09 05:26:12 -05:00
# undef LOCTEXT_NAMESPACE