Files
UnrealEngineUWP/Engine/Source/Editor/Sequencer/Private/SequencerUtilities.cpp
aurel cordonnier 8eebe8841f Merge UE5/RET @ 16305968 to UE5/Main
This represents UE4/Main @ 16261013 and Dev-PerfTest @ 16259937

[CL 16306996 by aurel cordonnier in ue5-main branch]
2021-05-12 18:10:03 -04:00

330 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SequencerUtilities.h"
#include "Misc/Paths.h"
#include "Layout/Margin.h"
#include "Fonts/SlateFontInfo.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SComboButton.h"
#include "Widgets/Input/SButton.h"
#include "Styling/CoreStyle.h"
#include "EditorStyleSet.h"
#include "SequencerTrackNode.h"
#include "MovieSceneTrack.h"
#include "MovieSceneSection.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "ISequencerTrackEditor.h"
#include "ISequencer.h"
#include "Sequencer.h"
#include "SequencerNodeTree.h"
#include "DisplayNodes/SequencerObjectBindingNode.h"
#include "ScopedTransaction.h"
#include "UObject/Package.h"
#include "AssetRegistryModule.h"
#include "FileHelpers.h"
#include "LevelSequence.h"
#define LOCTEXT_NAMESPACE "FSequencerUtilities"
static EVisibility GetRolloverVisibility(TAttribute<bool> HoverState, TWeakPtr<SComboButton> WeakComboButton)
{
TSharedPtr<SComboButton> ComboButton = WeakComboButton.Pin();
if (HoverState.Get() || ComboButton->IsOpen())
{
return EVisibility::SelfHitTestInvisible;
}
else
{
return EVisibility::Collapsed;
}
}
TSharedRef<SWidget> FSequencerUtilities::MakeAddButton(FText HoverText, FOnGetContent MenuContent, const TAttribute<bool>& HoverState, TWeakPtr<ISequencer> InSequencer)
{
FSlateFontInfo SmallLayoutFont = FCoreStyle::GetDefaultFontStyle("Regular", 8);
TSharedRef<STextBlock> ComboButtonText = SNew(STextBlock)
.Text(HoverText)
.Font(SmallLayoutFont)
.ColorAndOpacity( FSlateColor::UseForeground() );
TSharedRef<SComboButton> ComboButton =
SNew(SComboButton)
.HasDownArrow(false)
.IsFocusable(true)
.ButtonStyle(FEditorStyle::Get(), "HoverHintOnly")
.ForegroundColor( FSlateColor::UseForeground() )
.IsEnabled_Lambda([=]() { return InSequencer.IsValid() ? !InSequencer.Pin()->IsReadOnly() : false; })
.OnGetMenuContent(MenuContent)
.ContentPadding(FMargin(5, 2))
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.ButtonContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(FMargin(0,0,2,0))
[
SNew(SImage)
.ColorAndOpacity( FSlateColor::UseForeground() )
.Image(FEditorStyle::GetBrush("Plus"))
]
+ SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.AutoWidth()
[
ComboButtonText
]
];
TAttribute<EVisibility> Visibility = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateStatic(GetRolloverVisibility, HoverState, TWeakPtr<SComboButton>(ComboButton)));
ComboButtonText->SetVisibility(Visibility);
return ComboButton;
}
void FSequencerUtilities::PopulateMenu_CreateNewSection(FMenuBuilder& MenuBuilder, int32 RowIndex, UMovieSceneTrack* Track, TWeakPtr<ISequencer> InSequencer)
{
if (!Track)
{
return;
}
auto CreateNewSection = [Track, InSequencer, RowIndex](EMovieSceneBlendType BlendType)
{
TSharedPtr<ISequencer> Sequencer = InSequencer.Pin();
if (!Sequencer.IsValid())
{
return;
}
FQualifiedFrameTime CurrentTime = Sequencer->GetLocalTime();
TRange<double> VisibleRange = Sequencer->GetViewRange();
FScopedTransaction Transaction(LOCTEXT("AddSectionTransactionText", "Add Section"));
if (UMovieSceneSection* NewSection = Track->CreateNewSection())
{
int32 OverlapPriority = 0;
for (UMovieSceneSection* Section : Track->GetAllSections())
{
OverlapPriority = FMath::Max(Section->GetOverlapPriority() + 1, OverlapPriority);
// Move existing sections on the same row or beyond so that they don't overlap with the new section
if (Section != NewSection && Section->GetRowIndex() >= RowIndex)
{
Section->SetRowIndex(Section->GetRowIndex() + 1);
}
}
Track->Modify();
int32 DurationFrames = ( (VisibleRange.Size<double>() * 0.75) * CurrentTime.Rate ).FloorToFrame().Value;
NewSection->SetRange(TRange<FFrameNumber>(CurrentTime.Time.FrameNumber, CurrentTime.Time.FrameNumber + DurationFrames));
NewSection->SetOverlapPriority(OverlapPriority);
NewSection->SetRowIndex(RowIndex);
NewSection->SetBlendType(BlendType);
Track->AddSection(*NewSection);
Track->UpdateEasing();
Sequencer->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemAdded);
Sequencer->EmptySelection();
Sequencer->SelectSection(NewSection);
Sequencer->ThrobSectionSelection();
}
else
{
Transaction.Cancel();
}
};
FText NameOverride = Track->GetSupportedBlendTypes().Num() == 1 ? LOCTEXT("AddSectionText", "Add New Section") : FText();
FText TooltipOverride = Track->GetSupportedBlendTypes().Num() == 1 ? LOCTEXT("AddSectionToolTip", "Adds a new section at the current time") : FText();
const UEnum* MovieSceneBlendType = FindObjectChecked<UEnum>(ANY_PACKAGE, TEXT("EMovieSceneBlendType"));
for (EMovieSceneBlendType BlendType : Track->GetSupportedBlendTypes())
{
FText DisplayName = MovieSceneBlendType->GetDisplayNameTextByValue((int64)BlendType);
FName EnumValueName = MovieSceneBlendType->GetNameByValue((int64)BlendType);
MenuBuilder.AddMenuEntry(
NameOverride.IsEmpty() ? DisplayName : NameOverride,
TooltipOverride.IsEmpty() ? FText::Format(LOCTEXT("AddSectionFormatToolTip", "Adds a new {0} section at the current time"), DisplayName) : TooltipOverride,
FSlateIcon("EditorStyle", EnumValueName),
FUIAction(FExecuteAction::CreateLambda(CreateNewSection, BlendType))
);
}
}
void FSequencerUtilities::PopulateMenu_SetBlendType(FMenuBuilder& MenuBuilder, UMovieSceneSection* Section, TWeakPtr<ISequencer> InSequencer)
{
PopulateMenu_SetBlendType(MenuBuilder, TArray<TWeakObjectPtr<UMovieSceneSection>>({ Section }), InSequencer);
}
void FSequencerUtilities::PopulateMenu_SetBlendType(FMenuBuilder& MenuBuilder, const TArray<TWeakObjectPtr<UMovieSceneSection>>& InSections, TWeakPtr<ISequencer> InSequencer)
{
auto Execute = [InSections, InSequencer](EMovieSceneBlendType BlendType)
{
FScopedTransaction Transaction(LOCTEXT("SetBlendType", "Set Blend Type"));
for (TWeakObjectPtr<UMovieSceneSection> WeakSection : InSections)
{
if (UMovieSceneSection* Section = WeakSection.Get())
{
Section->Modify();
Section->SetBlendType(BlendType);
}
}
TSharedPtr<FSequencer> Sequencer = StaticCastSharedPtr<FSequencer>(InSequencer.Pin());
if (Sequencer.IsValid())
{
// If the blend type is changed to additive or relative, restore the state of the objects boud to this section before evaluating again.
// This allows the additive or relative to evaluate based on the initial values of the object, rather than the current animated values.
if (BlendType == EMovieSceneBlendType::Additive || BlendType == EMovieSceneBlendType::Relative)
{
TSet<UObject*> ObjectsToRestore;
TSharedRef<FSequencerNodeTree> SequencerNodeTree = Sequencer->GetNodeTree();
for (TWeakObjectPtr<UMovieSceneSection> WeakSection : InSections)
{
if (UMovieSceneSection* Section = WeakSection.Get())
{
TOptional<FSectionHandle> SectionHandle = SequencerNodeTree->GetSectionHandle(Section);
if (!SectionHandle)
{
continue;
}
TSharedPtr<FSequencerObjectBindingNode> ParentObjectBindingNode = SectionHandle->GetTrackNode()->FindParentObjectBindingNode();
if (!ParentObjectBindingNode.IsValid())
{
continue;
}
for (TWeakObjectPtr<> BoundObject : Sequencer->FindObjectsInCurrentSequence(ParentObjectBindingNode->GetObjectBinding()))
{
if (AActor* BoundActor = Cast<AActor>(BoundObject))
{
for (UActorComponent* Component : TInlineComponentArray<UActorComponent*>(BoundActor))
{
if (Component)
{
ObjectsToRestore.Add(Component);
}
}
}
ObjectsToRestore.Add(BoundObject.Get());
}
}
}
for (UObject* ObjectToRestore : ObjectsToRestore)
{
Sequencer->PreAnimatedState.RestorePreAnimatedState(*ObjectToRestore);
}
}
Sequencer->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::TrackValueChanged);
}
};
const UEnum* MovieSceneBlendType = FindObjectChecked<UEnum>(ANY_PACKAGE, TEXT("EMovieSceneBlendType"));
for (int32 NameIndex = 0; NameIndex < MovieSceneBlendType->NumEnums() - 1; ++NameIndex)
{
EMovieSceneBlendType BlendType = (EMovieSceneBlendType)MovieSceneBlendType->GetValueByIndex(NameIndex);
// Include this if any section supports it
bool bAnySupported = false;
for (TWeakObjectPtr<UMovieSceneSection> WeakSection : InSections)
{
UMovieSceneSection* Section = WeakSection.Get();
if (Section && Section->GetSupportedBlendTypes().Contains(BlendType))
{
bAnySupported = true;
break;
}
}
if (!bAnySupported)
{
continue;
}
FName EnumValueName = MovieSceneBlendType->GetNameByIndex(NameIndex);
MenuBuilder.AddMenuEntry(
MovieSceneBlendType->GetDisplayNameTextByIndex(NameIndex),
MovieSceneBlendType->GetToolTipTextByIndex(NameIndex),
FSlateIcon("EditorStyle", EnumValueName),
FUIAction(FExecuteAction::CreateLambda(Execute, BlendType))
);
}
}
FName FSequencerUtilities::GetUniqueName( FName CandidateName, const TArray<FName>& ExistingNames )
{
if (!ExistingNames.Contains(CandidateName))
{
return CandidateName;
}
FString CandidateNameString = CandidateName.ToString();
FString BaseNameString = CandidateNameString;
if ( CandidateNameString.Len() >= 3 && CandidateNameString.Right(3).IsNumeric() )
{
BaseNameString = CandidateNameString.Left( CandidateNameString.Len() - 3 );
}
FName UniqueName = FName(*BaseNameString);
int32 NameIndex = 1;
while ( ExistingNames.Contains( UniqueName ) )
{
UniqueName = FName( *FString::Printf(TEXT("%s%i"), *BaseNameString, NameIndex ) );
NameIndex++;
}
return UniqueName;
}
TArray<FString> FSequencerUtilities::GetAssociatedMapPackages(const ULevelSequence* InSequence)
{
if (!InSequence)
{
return TArray<FString>();
}
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
const FName LSMapPathName = *InSequence->GetOutermost()->GetPathName();
TArray<FString> AssociatedMaps;
TArray<FAssetIdentifier> AssociatedAssets;
// This makes the assumption these functions will append the array, and not clear it.
AssetRegistryModule.Get().GetReferencers(LSMapPathName, AssociatedAssets);
AssetRegistryModule.Get().GetDependencies(LSMapPathName, AssociatedAssets);
for (FAssetIdentifier& AssociatedMap : AssociatedAssets)
{
FString MapFilePath;
FString LevelPath = AssociatedMap.PackageName.ToString();
if (FEditorFileUtils::IsMapPackageAsset(LevelPath, MapFilePath))
{
AssociatedMaps.AddUnique(LevelPath);
}
}
AssociatedMaps.Sort([](const FString& One, const FString& Two) { return FPaths::GetBaseFilename(One) < FPaths::GetBaseFilename(Two); });
return AssociatedMaps;
}
#undef LOCTEXT_NAMESPACE