Files
UnrealEngineUWP/Engine/Source/Editor/Persona/Private/SAnimSegmentsPanel.cpp
robert manuszewski d1443992e1 Deprecating ANY_PACKAGE.
This change consists of multiple changes:

Core:
- Deprecation of ANY_PACKAGE macro. Added ANY_PACKAGE_DEPRECATED macro which can still be used for backwards compatibility purposes (only used in CoreUObject)
- Deprecation of StaticFindObjectFast* functions that take bAnyPackage parameter
- Added UStruct::GetStructPathName function that returns FTopLevelAssetPath representing the path name (package + object FName, super quick compared to UObject::GetPathName) + wrapper UClass::GetClassPathName to make it look better when used with UClasses
- Added (Static)FindFirstObject* functions that find a first object given its Name (no Outer). These functions are used in places I consider valid to do global UObject (UClass) lookups like parsing command line parameters / checking for unique object names
- Added static UClass::TryFindType function which serves a similar purpose as FindFirstObject however it's going to throw a warning (with a callstack / maybe ensure in the future?) if short class name is provided. This function is used  in places that used to use short class names but now should have been converted to use path names to catch any potential regressions and or edge cases I missed.
- Added static UClass::TryConvertShortNameToPathName utility function
- Added static UClass::TryFixShortClassNameExportPath utility function
- Object text export paths will now also include class path (Texture2D'/Game/Textures/Grass.Grass' -> /Script/Engine.Texture2D'/Game/Textures/Grass.Grass')
- All places that manually generated object export paths for objects will now use FObjectPropertyBase::GetExportPath
- Added a new startup test that checks for short type names in UClass/FProperty MetaData values

AssetRegistry:
- Deprecated any member variables (FAssetData / FARFilter) or functions that use FNames to represent class names and replaced them with FTopLevelAssetPath
- Added new member variables and new function overloads that use FTopLevelAssetPath to represent class names
- This also applies to a few other modules' APIs to match AssetRegistry changes

Everything else:
- Updated code that used ANY_PACKAGE (depending on the use case) to use FindObject(nullptr, PathToObject), UClass::TryFindType (used when path name is expected, warns if it's a short name) or FindFirstObject (usually for finding types based on user input but there's been a few legitimate use cases not related to user input)
- Updated code that used AssetRegistry API to use FTopLevelAssetPaths and USomeClass::StaticClass()->GetClassPathName() instead of GetFName()
- Updated meta data and hardcoded FindObject(ANY_PACKAGE, "EEnumNameOrClassName") calls to use path names

#jira UE-99463
#rb many.people
[FYI] Marcus.Wassmer
#preflight 629248ec2256738f75de9b32

#codereviewnumbers 20320742, 20320791, 20320799, 20320756, 20320809, 20320830, 20320840, 20320846, 20320851, 20320863, 20320780, 20320765, 20320876, 20320786

#ROBOMERGE-OWNER: robert.manuszewski
#ROBOMERGE-AUTHOR: robert.manuszewski
#ROBOMERGE-SOURCE: CL 20430220 via CL 20433854 via CL 20435474 via CL 20435484
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v949-20362246)

[CL 20448496 by robert manuszewski in ue5-main branch]
2022-06-01 03:46:59 -04:00

559 lines
19 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SAnimSegmentsPanel.h"
#include "Misc/MessageDialog.h"
#include "Modules/ModuleManager.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "AssetRegistry/AssetData.h"
#include "Animation/AnimSequenceBase.h"
#include "Animation/AnimSequence.h"
#include "Animation/AnimCompositeBase.h"
#include "ScopedTransaction.h"
#include "DragAndDrop/AssetDragDropOp.h"
#include "IContentBrowserSingleton.h"
#include "ContentBrowserModule.h"
#include "Framework/Application/SlateApplication.h"
#include "Subsystems/AssetEditorSubsystem.h"
#include "Editor.h"
#include "Widgets/Layout/SBox.h"
#define LOCTEXT_NAMESPACE "AnimSegmentPanel"
//////////////////////////////////////////////////////////////////////////
// SAnimSegmentPanel
void SAnimSegmentsPanel::Construct(const FArguments& InArgs)
{
bDragging = false;
const int32 NumTracks = 2;
AnimTrack = InArgs._AnimTrack;
SlotName = InArgs._SlotName;
ViewInputMin = InArgs._ViewInputMin;
ViewInputMax = InArgs._ViewInputMax;
OnAnimSegmentNodeClickedDelegate = InArgs._OnAnimSegmentNodeClicked;
OnPreAnimUpdateDelegate = InArgs._OnPreAnimUpdate;
OnPostAnimUpdateDelegate = InArgs._OnPostAnimUpdate;
OnAnimSegmentRemovedDelegate = InArgs._OnAnimSegmentRemoved;
OnAnimReplaceMapping = InArgs._OnAnimReplaceMapping;
OnDiffFromParentAsset = InArgs._OnDiffFromParentAsset;
OnGetNodeColor = InArgs._OnGetNodeColor;
bChildAnimMontage = InArgs._bChildAnimMontage;
// Register and bind ui commands
FAnimSegmentsPanelCommands::Register();
BindCommands();
// Empty out current widget array
TrackWidgets.Empty();
// Animation Segment tracks
TArray<TSharedPtr<STrackNode>> AnimNodes;
FLinearColor SelectedColor = FAppStyle::GetSlateColor("SelectionColor").GetSpecifiedColor();
TSharedPtr<SVerticalBox> AnimSegmentTracks;
ChildSlot
[
SAssignNew(AnimSegmentTracks, SVerticalBox)
];
for (int32 TrackIdx=0; TrackIdx < NumTracks; TrackIdx++)
{
TSharedPtr<STrack> AnimSegmentTrack;
if (bChildAnimMontage)
{
AnimSegmentTracks->AddSlot()
.AutoHeight()
.VAlign(VAlign_Center)
.Padding(0.0f)
[
SAssignNew(AnimSegmentTrack, STrack)
.ViewInputMin(ViewInputMin)
.ViewInputMax(ViewInputMax)
.TrackMaxValue(InArgs._TrackMaxValue)
.TrackNumDiscreteValues(InArgs._TrackNumDiscreteValues)
.OnTrackRightClickContextMenu(InArgs._OnTrackRightClickContextMenu)
.OnTrackDragDrop(this, &SAnimSegmentsPanel::OnTrackDragDrop)
];
}
else
{
AnimSegmentTracks->AddSlot()
.AutoHeight()
.VAlign(VAlign_Center)
.Padding(0.0f)
[
SAssignNew(AnimSegmentTrack, STrack)
.ViewInputMin(ViewInputMin)
.ViewInputMax(ViewInputMax)
.TrackMaxValue(InArgs._TrackMaxValue)
//section stuff
.OnBarDrag(InArgs._OnBarDrag)
.OnBarDrop(InArgs._OnBarDrop)
.OnBarClicked(InArgs._OnBarClicked)
.DraggableBars(InArgs._DraggableBars)
.DraggableBarSnapPositions(InArgs._DraggableBarSnapPositions)
.TrackNumDiscreteValues(InArgs._TrackNumDiscreteValues)
.OnTrackRightClickContextMenu(InArgs._OnTrackRightClickContextMenu)
.OnTrackDragDrop(this, &SAnimSegmentsPanel::OnTrackDragDrop)
];
}
TrackWidgets.Add(AnimSegmentTrack);
}
// Generate Nodes and map them to tracks
for ( int32 SegmentIdx=0; SegmentIdx < AnimTrack->AnimSegments.Num(); SegmentIdx++ )
{
if (bChildAnimMontage)
{
TrackWidgets[SegmentIdx % TrackWidgets.Num()]->AddTrackNode(
SNew(STrackNode)
.ViewInputMax(this->ViewInputMax)
.ViewInputMin(this->ViewInputMin)
.NodeColor(this, &SAnimSegmentsPanel::GetNodeColor, SegmentIdx)
.SelectedNodeColor(SelectedColor)
.DataLength(this, &SAnimSegmentsPanel::GetSegmentLength, SegmentIdx)
.DataStartPos(this, &SAnimSegmentsPanel::GetSegmentStartPos, SegmentIdx)
.NodeName(this, &SAnimSegmentsPanel::GetAnimSegmentName, SegmentIdx)
.ToolTipText(this, &SAnimSegmentsPanel::GetAnimSegmentDetailedInfo, SegmentIdx)
.OnTrackNodeDropped(this, &SAnimSegmentsPanel::OnSegmentDropped, SegmentIdx)
.OnNodeRightClickContextMenu(this, &SAnimSegmentsPanel::SummonSegmentNodeContextMenu, SegmentIdx)
.NodeSelectionSet(InArgs._NodeSelectionSet)
);
}
else
{
TrackWidgets[SegmentIdx % TrackWidgets.Num()]->AddTrackNode(
SNew(STrackNode)
.ViewInputMax(this->ViewInputMax)
.ViewInputMin(this->ViewInputMin)
.NodeColor(this, &SAnimSegmentsPanel::GetNodeColor, SegmentIdx)
.SelectedNodeColor(SelectedColor)
.DataLength(this, &SAnimSegmentsPanel::GetSegmentLength, SegmentIdx)
.DataStartPos(this, &SAnimSegmentsPanel::GetSegmentStartPos, SegmentIdx)
.NodeName(this, &SAnimSegmentsPanel::GetAnimSegmentName, SegmentIdx)
.ToolTipText(this, &SAnimSegmentsPanel::GetAnimSegmentDetailedInfo, SegmentIdx)
.OnTrackNodeDragged(this, &SAnimSegmentsPanel::SetSegmentStartPos, SegmentIdx)
.OnTrackNodeDropped(this, &SAnimSegmentsPanel::OnSegmentDropped, SegmentIdx)
.OnNodeRightClickContextMenu(this, &SAnimSegmentsPanel::SummonSegmentNodeContextMenu, SegmentIdx)
.OnTrackNodeClicked(this, &SAnimSegmentsPanel::OnAnimSegmentNodeClicked, SegmentIdx)
.NodeSelectionSet(InArgs._NodeSelectionSet)
);
}
}
}
bool SAnimSegmentsPanel::ValidIndex(int32 AnimSegmentIndex) const
{
return (AnimTrack && AnimTrack->AnimSegments.IsValidIndex(AnimSegmentIndex));
}
FLinearColor SAnimSegmentsPanel::GetNodeColor(int32 AnimSegmentIndex) const
{
static const FLinearColor DisabledColor(64, 64, 64);
static const FLinearColor ChildMontageColor(128, 255, 0);
if (ValidIndex(AnimSegmentIndex) && AnimTrack->AnimSegments[AnimSegmentIndex].IsValid())
{
bool bUseModifiedChildColor = bChildAnimMontage && OnDiffFromParentAsset.IsBound()
&& OnDiffFromParentAsset.Execute(SlotName, AnimSegmentIndex, AnimTrack->AnimSegments[AnimSegmentIndex]);
if (bUseModifiedChildColor)
{
return ChildMontageColor;
}
else if(OnGetNodeColor.IsBound())
{
return OnGetNodeColor.Execute(AnimTrack->AnimSegments[AnimSegmentIndex]);
}
}
return DisabledColor;
}
float SAnimSegmentsPanel::GetSegmentLength(int32 AnimSegmentIndex) const
{
if (ValidIndex(AnimSegmentIndex))
{
return AnimTrack->AnimSegments[AnimSegmentIndex].GetLength();
}
return 0.f;
}
float SAnimSegmentsPanel::GetSegmentStartPos(int32 AnimSegmentIndex) const
{
if (ValidIndex(AnimSegmentIndex))
{
return AnimTrack->AnimSegments[AnimSegmentIndex].StartPos;
}
return 0.f;
}
FString SAnimSegmentsPanel::GetAnimSegmentName(int32 AnimSegmentIndex) const
{
if (ValidIndex(AnimSegmentIndex))
{
FString TitleLabel;
if(const UAnimSequenceBase* AnimReference = AnimTrack->AnimSegments[AnimSegmentIndex].GetAnimReference())
{
FString AssetName = AnimReference->GetName();
if (AnimTrack->AnimSegments[AnimSegmentIndex].IsValid() == false)
{
TitleLabel = FString::Printf(TEXT("Error : %s"), *AssetName);
}
else if (bChildAnimMontage)
{
TitleLabel = FString::Printf(TEXT("Child : %s"), *AssetName);
}
else
{
TitleLabel = AssetName;
}
return TitleLabel;
}
}
return FString();
}
FText SAnimSegmentsPanel::GetAnimSegmentDetailedInfo(int32 AnimSegmentIndex) const
{
if (ValidIndex(AnimSegmentIndex))
{
FAnimSegment& AnimSegment = AnimTrack->AnimSegments[AnimSegmentIndex];
if (const UAnimSequenceBase* AnimReference = AnimTrack->AnimSegments[AnimSegmentIndex].GetAnimReference())
{
static const FNumberFormattingOptions FormatOptions = FNumberFormattingOptions()
.SetMinimumFractionalDigits(2)
.SetMaximumFractionalDigits(2);
if (AnimTrack->AnimSegments[AnimSegmentIndex].IsValid())
{
return FText::Format(LOCTEXT("AnimSegmentPanel_GetAnimSegmentDetailedInfoFmt", "{0} {1} {2}"), FText::FromString(AnimReference->GetName()), FText::AsNumber(AnimSegment.GetLength(), &FormatOptions),
AnimTrack->AnimSegments[AnimSegmentIndex].IsPlayLengthOutOfDate() ? LOCTEXT("AnimSegmentPanel_GetAnimSegmentDetailedInfoFmt_Warning_PlayTimeIncorrect", "(segment length does not match animation play length)") : FText::FromString(FString()));
}
else
{
return FText::Format(LOCTEXT("AnimSegmentPanel_GetAnimSegmentDetailedInfoFmt_Error_RecursiveReference", "{0} {1} - ERROR: Recursive Reference Found"), FText::FromString(AnimReference->GetName()), FText::AsNumber(AnimSegment.GetLength(), &FormatOptions));
}
}
}
return FText::GetEmpty();
}
void SAnimSegmentsPanel::SetSegmentStartPos(float NewStartPos, int32 AnimSegmentIndex)
{
if (ValidIndex(AnimSegmentIndex))
{
if(!bDragging)
{
const FScopedTransaction Transaction( LOCTEXT("AnimSegmentPanel_SetSegmentStart", "Edit Segment Start Time") );
OnPreAnimUpdateDelegate.ExecuteIfBound();
bDragging = true;
}
AnimTrack->AnimSegments[AnimSegmentIndex].StartPos = NewStartPos;
AnimTrack->CollapseAnimSegments();
}
}
void SAnimSegmentsPanel::OnSegmentDropped(int32 AnimSegmentIndex)
{
if(bDragging)
{
bDragging = false;
OnPostAnimUpdateDelegate.ExecuteIfBound();
}
}
void SAnimSegmentsPanel::SummonSegmentNodeContextMenu(FMenuBuilder& MenuBuilder, int32 AnimSegmentIndex)
{
FUIAction UIAction;
if (bChildAnimMontage)
{
MenuBuilder.BeginSection("AnimSegmentsLabel", LOCTEXT("Anim Segment", "Anim Segment"));
{
// if different than parent
UIAction.ExecuteAction.BindRaw(this, &SAnimSegmentsPanel::RevertToParent, AnimSegmentIndex);
MenuBuilder.AddMenuEntry(LOCTEXT("RevertToParentSegment", "Revert To Parent"), LOCTEXT("RevertToParentSegment_ToolTip", "Revert to Parent Animation"), FSlateIcon(), UIAction);
MenuBuilder.AddSubMenu(LOCTEXT("PickAnimationForTheSegment", "Replace animation with..."), LOCTEXT("PickAnimationForTheSegment_TooTip", "Replace the current animation with another animation."), FNewMenuDelegate::CreateSP(this, &SAnimSegmentsPanel::FillSubMenu, AnimSegmentIndex));
MenuBuilder.AddMenuSeparator();
// open asset option
UIAction.ExecuteAction.BindRaw(this, &SAnimSegmentsPanel::OpenAsset, AnimSegmentIndex);
MenuBuilder.AddMenuEntry(LOCTEXT("OpenAssetOfSegment", "Open Asset"), LOCTEXT("OpenAssetOfSegment_ToolTip", "Open Asset"), FSlateIcon(), UIAction);
}
MenuBuilder.EndSection();
}
else
{
MenuBuilder.BeginSection("AnimSegmentsLabel", LOCTEXT("Anim Segment", "Anim Segment"));
{
UIAction.ExecuteAction.BindRaw(this, &SAnimSegmentsPanel::RemoveAnimSegment, AnimSegmentIndex);
MenuBuilder.AddMenuEntry(LOCTEXT("DeleteSegment", "Delete Segment"), LOCTEXT("DeleteSegmentHint", "Delete Segment"), FSlateIcon(), UIAction);
// open asset option
UIAction.ExecuteAction.BindRaw(this, &SAnimSegmentsPanel::OpenAsset, AnimSegmentIndex);
MenuBuilder.AddMenuEntry(LOCTEXT("OpenAssetOfSegment", "Open Asset"), LOCTEXT("OpenAssetOfSegment_ToolTip", "Open Asset"), FSlateIcon(), UIAction);
}
MenuBuilder.EndSection();
}
}
void SAnimSegmentsPanel::AddAnimSegment( UAnimSequenceBase* NewSequenceBase, float NewStartPos )
{
const FScopedTransaction Transaction( LOCTEXT("AnimSegmentPanel_AddSegment", "Add Segment") );
OnPreAnimUpdateDelegate.ExecuteIfBound();
FAnimSegment NewSegment;
NewSegment.SetAnimReference(NewSequenceBase, true);
NewSegment.StartPos = NewStartPos;
AnimTrack->AnimSegments.Add(NewSegment);
OnPostAnimUpdateDelegate.ExecuteIfBound();
}
void SAnimSegmentsPanel::ReplaceAnimSegment(int32 AnimSegmentIndex, UAnimSequenceBase* NewSequenceBase)
{
const FScopedTransaction Transaction(LOCTEXT("AnimSegmentPanel_ReplaceSegment", "Replace Segment"));
if (AnimTrack->AnimSegments.IsValidIndex(AnimSegmentIndex))
{
UAnimSequenceBase* OldSequenceBase = AnimTrack->AnimSegments[AnimSegmentIndex].GetAnimReference();
if (OldSequenceBase != NewSequenceBase)
{
OnPreAnimUpdateDelegate.ExecuteIfBound();
OnAnimReplaceMapping.ExecuteIfBound(SlotName, AnimSegmentIndex, OldSequenceBase, NewSequenceBase);
OnPostAnimUpdateDelegate.ExecuteIfBound();
}
}
// it doesn't work well if I leave the window open. The delegate goes weired or it stop showing the popups.
FSlateApplication::Get().DismissAllMenus();
}
void SAnimSegmentsPanel::ReplaceAnimSegment(const FAssetData& NewSequenceData, int32 AnimSegmentIndex)
{
UAnimSequenceBase* NewSequenceBase = Cast<UAnimSequenceBase> (NewSequenceData.GetAsset());
if (NewSequenceBase)
{
ReplaceAnimSegment(AnimSegmentIndex, NewSequenceBase);
}
}
void SAnimSegmentsPanel::ReplaceAnimSegment(UAnimSequenceBase* NewSequenceBase, float NewStartPos)
{
int32 SegmentIdx = AnimTrack->GetSegmentIndexAtTime(NewStartPos);
ReplaceAnimSegment(SegmentIdx, NewSequenceBase);
}
bool SAnimSegmentsPanel::IsValidToAdd(UAnimSequenceBase* NewSequenceBase) const
{
if (AnimTrack == NULL || NewSequenceBase == NULL)
{
return false;
}
return (AnimTrack->IsValidToAdd(NewSequenceBase));
}
void SAnimSegmentsPanel::RemoveAnimSegment(int32 AnimSegmentIndex)
{
if(ValidIndex(AnimSegmentIndex))
{
const FScopedTransaction Transaction( LOCTEXT("AnimSegmentseEditor", "Remove Segment") );
OnPreAnimUpdateDelegate.ExecuteIfBound();
AnimTrack->AnimSegments.RemoveAt(AnimSegmentIndex);
OnAnimSegmentRemovedDelegate.ExecuteIfBound(AnimSegmentIndex);
OnPostAnimUpdateDelegate.ExecuteIfBound();
}
}
void SAnimSegmentsPanel::RevertToParent(int32 AnimSegmentIndex)
{
ReplaceAnimSegment(AnimSegmentIndex, nullptr);
}
void SAnimSegmentsPanel::OpenAsset(int32 AnimSegmentIndex)
{
if (ValidIndex(AnimSegmentIndex))
{
if (UAnimSequenceBase* Asset = AnimTrack->AnimSegments[AnimSegmentIndex].GetAnimReference())
{
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(Asset);
}
}
}
void SAnimSegmentsPanel::FillSubMenu(FMenuBuilder& MenuBuilder, int32 AnimSegmentIndex)
{
if (ValidIndex(AnimSegmentIndex))
{
UAnimSequenceBase* OldSequenceBase = AnimTrack->AnimSegments[AnimSegmentIndex].GetAnimReference();
if (ensureAlways(OldSequenceBase))
{
FAssetPickerConfig AssetPickerConfig;
/** The asset picker will only show skeletons */
AssetPickerConfig.Filter.ClassPaths.Add(OldSequenceBase->GetClass()->GetClassPathName());
AssetPickerConfig.Filter.bRecursiveClasses = false;
AssetPickerConfig.bAllowNullSelection = false;
USkeleton* Skeleton = OldSequenceBase->GetSkeleton();
AssetPickerConfig.Filter.TagsAndValues.Add(TEXT("Skeleton"), FAssetData(Skeleton).GetExportTextName());
// only do this for anim sequence because we don't know additive or not otherwise from asset registry
bool bFilterAdditive = OldSequenceBase->GetClass() == UAnimSequence::StaticClass();
if (bFilterAdditive)
{
// we do just check additiveanimtype, not IsValidAdditive because we only check asset registry string, we just assume this only checks additive anim type
// in order to check IsValidAdditive, we have to load all animations, which is too slow
AssetPickerConfig.OnShouldFilterAsset = FOnShouldFilterAsset::CreateRaw(this, &SAnimSegmentsPanel::ShouldFilter, CastChecked<UAnimSequence>(OldSequenceBase)->AdditiveAnimType);
}
/** The delegate that fires when an asset was selected */
AssetPickerConfig.OnAssetSelected = FOnAssetSelected::CreateRaw(this, &SAnimSegmentsPanel::ReplaceAnimSegment, AnimSegmentIndex);
/** The default view mode should be a list view */
AssetPickerConfig.InitialAssetViewType = EAssetViewType::List;
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
MenuBuilder.AddWidget(
SNew(SBox)
.MaxDesiredHeight(600.0f)
[
ContentBrowserModule.Get().CreateAssetPicker(AssetPickerConfig)
],
FText(),
true
);
}
}
}
bool SAnimSegmentsPanel::ShouldFilter(const FAssetData& DataToDisplay, TEnumAsByte<EAdditiveAnimationType> InAdditiveType)
{
UEnum* AdditiveTypeEnum = StaticEnum<EAdditiveAnimationType>();
const FString EnumString = DataToDisplay.GetTagValueRef<FString>(GET_MEMBER_NAME_CHECKED(UAnimSequence, AdditiveAnimType));
EAdditiveAnimationType AdditiveType = (!EnumString.IsEmpty() ? (EAdditiveAnimationType)AdditiveTypeEnum->GetValueByName(*EnumString) : AAT_None);
return (AdditiveType != InAdditiveType);
}
void SAnimSegmentsPanel::OnTrackDragDrop( TSharedPtr<FDragDropOperation> DragDropOp, float DataPos )
{
if (DragDropOp.IsValid() && DragDropOp->IsOfType<FAssetDragDropOp>())
{
TSharedPtr<FAssetDragDropOp> AssetOp = StaticCastSharedPtr<FAssetDragDropOp>(DragDropOp);
if (AssetOp->HasAssets())
{
bool bFailedToAdd = false;
if(bChildAnimMontage)
{
UAnimSequenceBase* DroppedSequence = FAssetData::GetFirstAsset<UAnimSequenceBase>(AssetOp->GetAssets());
if (IsValidToAdd(DroppedSequence))
{
ReplaceAnimSegment(DroppedSequence, DataPos);
}
else
{
bFailedToAdd = true;
}
}
else
{
const FScopedTransaction Transaction( LOCTEXT("AnimSegmentPanel_AddSegments", "Add Segments") );
for(const FAssetData& DroppedAssetData : AssetOp->GetAssets())
{
UAnimSequenceBase* DroppedSequence = Cast<UAnimSequenceBase>(DroppedAssetData.GetAsset());
if (IsValidToAdd(DroppedSequence))
{
AddAnimSegment(DroppedSequence, DataPos);
}
else
{
bFailedToAdd = true;
}
}
}
if(bFailedToAdd)
{
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("FailedToAdd", "Make sure the target animations are valid. Check to make sure if they are the same additive type if additive."));
}
}
}
}
void SAnimSegmentsPanel::OnAnimSegmentNodeClicked(int32 SegmentIdx)
{
OnAnimSegmentNodeClickedDelegate.ExecuteIfBound(SegmentIdx);
}
void SAnimSegmentsPanel::RemoveSelectedAnimSegments()
{
TArray<int32> SelectedNodeIndices;
for(int i = 0 ; i < TrackWidgets.Num() ; ++i)
{
TSharedPtr<STrack> Track = TrackWidgets[i];
Track->GetSelectedNodeIndices(SelectedNodeIndices);
// Reverse order to preserve indices
for(int32 j = SelectedNodeIndices.Num() - 1 ; j >= 0 ; --j)
{
// Segments are placed on one of two tracks with the first segment always residing
// in track 0 - need to modify the index from track and index to data index.
int32 ModifiedIndex = i + 2 * SelectedNodeIndices[j];
RemoveAnimSegment(ModifiedIndex);
}
}
}
void SAnimSegmentsPanel::BindCommands()
{
check(!UICommandList.IsValid());
UICommandList = MakeShareable(new FUICommandList);
const FAnimSegmentsPanelCommands& Commands = FAnimSegmentsPanelCommands::Get();
// do not allow to delete if child anim montage
if (!bChildAnimMontage)
{
UICommandList->MapAction(
Commands.DeleteSegment,
FExecuteAction::CreateSP(this, &SAnimSegmentsPanel::RemoveSelectedAnimSegments));
}
}
FReply SAnimSegmentsPanel::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{
if(UICommandList->ProcessCommandBindings(InKeyEvent))
{
return FReply::Handled();
}
return FReply::Unhandled();
}
void FAnimSegmentsPanelCommands::RegisterCommands()
{
// this is here for key handling
UI_COMMAND(DeleteSegment, "Delete", "Deletes the selected segment", EUserInterfaceActionType::Button, FInputChord(EKeys::Platform_Delete));
}
#undef LOCTEXT_NAMESPACE