// Copyright Epic Games, Inc. All Rights Reserved. #include "CoreMinimal.h" #include "Modules/ModuleManager.h" #include "Textures/SlateIcon.h" #include "EditorModeRegistry.h" #include "Toolkits/AssetEditorToolkit.h" #include "IMovieRendererInterface.h" #include "ISequencer.h" #include "ISequencerModule.h" #include "SequencerCommands.h" #include "ISequencerObjectChangeListener.h" #include "Sequencer.h" #include "SequencerCustomizationManager.h" #include "SequencerEdMode.h" #include "SequencerObjectChangeListener.h" #include "IDetailKeyframeHandler.h" #include "IDetailTreeNode.h" #include "IDetailsView.h" #include "Tree/CurveEditorTreeFilter.h" #include "AnimatedPropertyKey.h" #include "MovieSceneSignedObject.h" #include "MVVM/CurveEditorIntegrationExtension.h" #include "MVVM/FolderModelStorageExtension.h" #include "MVVM/ObjectBindingModelStorageExtension.h" #include "MVVM/SectionModelStorageExtension.h" #include "MVVM/TrackModelStorageExtension.h" #include "MVVM/TrackRowModelStorageExtension.h" #include "MVVM/ViewModels/SequenceModel.h" #include "ToolMenus.h" #include "ContentBrowserMenuContexts.h" #include "SequencerUtilities.h" #include "FileHelpers.h" #include "LevelSequence.h" #include "Misc/CoreDelegates.h" #include "UnrealEdGlobals.h" #include "Editor/UnrealEdEngine.h" #include "Editor/TransBuffer.h" #if !IS_MONOLITHIC UE::MovieScene::FEntityManager*& GEntityManagerForDebugging = UE::MovieScene::GEntityManagerForDebuggingVisualizers; #endif #define LOCTEXT_NAMESPACE "SequencerEditor" namespace UE { namespace Sequencer { struct FDeferredSignedObjectChangeHandler : UE::MovieScene::IDeferredSignedObjectChangeHandler, FGCObject { FDeferredSignedObjectChangeHandler() { Init(); } ~FDeferredSignedObjectChangeHandler() { if (UTransBuffer* TransBuffer = WeakBuffer.Get()) { TransBuffer->OnTransactionStateChanged().RemoveAll(this); } } void Init() { UTransBuffer* TransBuffer = GUnrealEd ? Cast(GUnrealEd->Trans) : nullptr; if (TransBuffer) { WeakBuffer = TransBuffer; TransBuffer->OnTransactionStateChanged().AddRaw(this, &FDeferredSignedObjectChangeHandler::OnTransactionStateChanged); if (TransBuffer->IsActive()) { DeferTransactionChanges.Emplace(); } } else { FCoreDelegates::OnPostEngineInit.AddLambda([this]{ this->Init(); }); } } void OnTransactionStateChanged(const FTransactionContext& TransactionContext, ETransactionStateEventType TransactionState) { /** A transaction has been started. This will be followed by a TransactionCanceled or TransactionFinalized event. */ switch (TransactionState) { case ETransactionStateEventType::TransactionStarted: case ETransactionStateEventType::UndoRedoStarted: DeferTransactionChanges.Emplace(); break; case ETransactionStateEventType::TransactionCanceled: case ETransactionStateEventType::TransactionFinalized: case ETransactionStateEventType::UndoRedoFinalized: DeferTransactionChanges.Reset(); break; } } void Flush() override { for (TWeakObjectPtr WeakObject : SignedObjects) { if (UMovieSceneSignedObject* Object = WeakObject.Get()) { Object->BroadcastChanged(); } } SignedObjects.Empty(); } void DeferMarkAsChanged(UMovieSceneSignedObject* SignedObject) override { SignedObjects.Add(SignedObject); } void AddReferencedObjects( FReferenceCollector& Collector ) override { for (TWeakObjectPtr WeakObject : SignedObjects) { if (UMovieSceneSignedObject* Object = WeakObject.Get()) { Collector.AddReferencedObject(Object); } } } bool CreateImplicitScopedModifyDefer() override { ensure(!DeferImplicitChanges.IsSet()); DeferImplicitChanges.Emplace(); return true; } void ResetImplicitScopedModifyDefer() override { DeferImplicitChanges.Reset(); } FString GetReferencerName() const override { return TEXT("FDeferredSignedObjectChangeHandler"); } TSet> SignedObjects; TWeakObjectPtr WeakBuffer; TOptional DeferTransactionChanges; TOptional DeferImplicitChanges; }; } // namespace Sequencer } // namespace UE // Destructor defined in CPP to avoid having to #include SequencerChannelInterface.h in the main module definition ISequencerModule::~ISequencerModule() { } ECurveEditorTreeFilterType ISequencerModule::GetSequencerSelectionFilterType() { static ECurveEditorTreeFilterType FilterType = FCurveEditorTreeFilter::RegisterFilterType(); return FilterType; } static TSharedPtr GetKeyframeHandler(TWeakPtr OwnerTreeNode) { TSharedPtr OwnerTreeNodePtr = OwnerTreeNode.Pin(); if (!OwnerTreeNodePtr.IsValid()) { return TSharedPtr(); } IDetailsView* DetailsView = OwnerTreeNodePtr->GetNodeDetailsView(); if (DetailsView == nullptr) { return TSharedPtr(); } return DetailsView->GetKeyframeHandler(); } static bool IsKeyframeButtonVisible(TWeakPtr OwnerTreeNode, TSharedPtr PropertyHandle) { TSharedPtr KeyframeHandler = GetKeyframeHandler(OwnerTreeNode); if (!KeyframeHandler.IsValid() || !PropertyHandle.IsValid()) { return false; } const UClass* ObjectClass = PropertyHandle->GetOuterBaseClass(); if (ObjectClass == nullptr) { return false; } return KeyframeHandler->IsPropertyKeyable(ObjectClass, *PropertyHandle); } static bool IsKeyframeButtonEnabled(TWeakPtr OwnerTreeNode) { TSharedPtr KeyframeHandler = GetKeyframeHandler(OwnerTreeNode); if (!KeyframeHandler.IsValid()) { return false; } return KeyframeHandler->IsPropertyKeyingEnabled(); } static void OnAddKeyframeClicked(TWeakPtr OwnerTreeNode, TSharedPtr PropertyHandle) { TSharedPtr KeyframeHandler = GetKeyframeHandler(OwnerTreeNode); if (!KeyframeHandler.IsValid() || !PropertyHandle.IsValid()) { return; } KeyframeHandler->OnKeyPropertyClicked(*PropertyHandle); } static void RegisterKeyframeExtensionHandler(const FOnGenerateGlobalRowExtensionArgs& Args, TArray& OutExtensionButtons) { // local copy for capturing in handlers below TSharedPtr PropertyHandle = Args.PropertyHandle; if (!PropertyHandle.IsValid()) { return; } static FSlateIcon CreateKeyIcon(FAppStyle::Get().GetStyleSetName(), "Sequencer.AddKey.Details"); TWeakPtr OwnerTreeNode = Args.OwnerTreeNode; FPropertyRowExtensionButton& CreateKey = OutExtensionButtons.AddDefaulted_GetRef(); CreateKey.Icon = CreateKeyIcon; CreateKey.Label = NSLOCTEXT("PropertyEditor", "CreateKey", "Create Key"); CreateKey.ToolTip = NSLOCTEXT("PropertyEditor", "CreateKeyToolTip", "Add a keyframe for this property."); CreateKey.UIAction = FUIAction( FExecuteAction::CreateStatic(&OnAddKeyframeClicked, OwnerTreeNode, PropertyHandle), FCanExecuteAction::CreateStatic(&IsKeyframeButtonEnabled, OwnerTreeNode), FGetActionCheckState(), FIsActionButtonVisible::CreateStatic(&IsKeyframeButtonVisible, OwnerTreeNode, PropertyHandle) ); } /** * SequencerModule implementation (private) */ class FSequencerModule : public ISequencerModule { public: // ISequencerModule interface virtual TSharedRef CreateSequencer(const FSequencerInitParams& InitParams) override { TSharedRef Sequencer = MakeShared(); TSharedRef ObjectChangeListener = MakeShared(Sequencer); OnPreSequencerInit.Broadcast(Sequencer, ObjectChangeListener, InitParams); Sequencer->InitSequencer(InitParams, ObjectChangeListener, TrackEditorDelegates, EditorObjectBindingDelegates); OnSequencerCreated.Broadcast(Sequencer); return Sequencer; } virtual FDelegateHandle RegisterTrackEditor( FOnCreateTrackEditor InOnCreateTrackEditor, TArrayView AnimatedPropertyTypes ) override { TrackEditorDelegates.Add( InOnCreateTrackEditor ); FDelegateHandle Handle = TrackEditorDelegates.Last().GetHandle(); for (const FAnimatedPropertyKey& Key : AnimatedPropertyTypes) { PropertyAnimators.Add(Key); } if (AnimatedPropertyTypes.Num() > 0) { FAnimatedTypeCache CachedTypes; CachedTypes.FactoryHandle = Handle; for (const FAnimatedPropertyKey& Key : AnimatedPropertyTypes) { CachedTypes.AnimatedTypes.Add(Key); } AnimatedTypeCache.Add(CachedTypes); } return Handle; } virtual void UnRegisterTrackEditor( FDelegateHandle InHandle ) override { TrackEditorDelegates.RemoveAll( [=](const FOnCreateTrackEditor& Delegate){ return Delegate.GetHandle() == InHandle; } ); int32 CacheIndex = AnimatedTypeCache.IndexOfByPredicate([=](const FAnimatedTypeCache& In) { return In.FactoryHandle == InHandle; }); if (CacheIndex != INDEX_NONE) { for (const FAnimatedPropertyKey& Key : AnimatedTypeCache[CacheIndex].AnimatedTypes) { PropertyAnimators.Remove(Key); } AnimatedTypeCache.RemoveAtSwap(CacheIndex); } } virtual FDelegateHandle RegisterTrackModel(FOnCreateTrackModel InCreator) override { TrackModelDelegates.Add(InCreator); return TrackModelDelegates.Last().GetHandle(); } virtual void UnregisterTrackModel(FDelegateHandle InHandle) override { TrackModelDelegates.RemoveAll([=](const FOnCreateTrackModel& Delegate) { return Delegate.GetHandle() == InHandle; }); } virtual FDelegateHandle RegisterOnSequencerCreated(FOnSequencerCreated::FDelegate InOnSequencerCreated) override { return OnSequencerCreated.Add(InOnSequencerCreated); } virtual void UnregisterOnSequencerCreated(FDelegateHandle InHandle) override { OnSequencerCreated.Remove(InHandle); } virtual FDelegateHandle RegisterOnPreSequencerInit(FOnPreSequencerInit::FDelegate InOnPreSequencerInit) override { return OnPreSequencerInit.Add(InOnPreSequencerInit); } virtual void UnregisterOnPreSequencerInit(FDelegateHandle InHandle) override { OnPreSequencerInit.Remove(InHandle); } virtual FDelegateHandle RegisterEditorObjectBinding(FOnCreateEditorObjectBinding InOnCreateEditorObjectBinding) override { EditorObjectBindingDelegates.Add(InOnCreateEditorObjectBinding); return EditorObjectBindingDelegates.Last().GetHandle(); } virtual void UnRegisterEditorObjectBinding(FDelegateHandle InHandle) override { EditorObjectBindingDelegates.RemoveAll([=](const FOnCreateEditorObjectBinding& Delegate) { return Delegate.GetHandle() == InHandle; }); } void RegisterMenus() { UToolMenus* ToolMenus = UToolMenus::Get(); UToolMenu* Menu = ToolMenus->ExtendMenu("ContentBrowser.AssetContextMenu.LevelSequence"); if (!Menu) { return; } FToolMenuSection& Section = Menu->FindOrAddSection("GetAssetActions"); Section.AddDynamicEntry("SequencerActions", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { UContentBrowserAssetContextMenuContext* Context = InSection.FindContext(); if (!Context) { return; } ULevelSequence* LevelSequence = Context->SelectedObjects.Num() == 1 ? Cast(Context->SelectedObjects[0]) : nullptr; if (LevelSequence) { // if this LevelSequence has associated maps, offer to load them TArray AssociatedMaps = FSequencerUtilities::GetAssociatedMapPackages(LevelSequence); if(AssociatedMaps.Num()>0) { InSection.AddSubMenu( "SequencerOpenMap_Label", LOCTEXT("SequencerOpenMap_Label", "Open Map"), LOCTEXT("SequencerOpenMap_Tooltip", "Open a map associated with this Level Sequence Asset"), FNewMenuDelegate::CreateLambda( [AssociatedMaps](FMenuBuilder& SubMenuBuilder) { for (const FString& AssociatedMap : AssociatedMaps) { SubMenuBuilder.AddMenuEntry( FText::FromString(FPaths::GetBaseFilename(AssociatedMap)), FText(), FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Levels"), FExecuteAction::CreateLambda( [AssociatedMap] { FEditorFileUtils::LoadMap(AssociatedMap); } ) ); } } ), false, FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Levels") ); } } })); } virtual void StartupModule() override { using namespace UE::Sequencer; using namespace UE::MovieScene; if (GIsEditor) { FEditorModeRegistry::Get().RegisterMode( FSequencerEdMode::EM_SequencerMode, NSLOCTEXT("Sequencer", "SequencerEditMode", "Sequencer Mode"), FSlateIcon(), false); if (UToolMenus::TryGet()) { FSequencerCommands::Register(); RegisterMenus(); } else { FCoreDelegates::OnPostEngineInit.AddStatic(&FSequencerCommands::Register); FCoreDelegates::OnPostEngineInit.AddRaw(this, &FSequencerModule::RegisterMenus); } UMovieSceneSignedObject::SetDeferredHandler(MakeUnique()); FPropertyEditorModule& EditModule = FModuleManager::Get().GetModuleChecked("PropertyEditor"); OnGetGlobalRowExtensionHandle = EditModule.GetGlobalRowExtensionDelegate().AddStatic(&RegisterKeyframeExtensionHandler); } FSequenceModel::CreateExtensionsEvent.AddLambda( [&](FSequenceModel* InModel) { InModel->AddDynamicExtension(FCurveEditorIntegrationExtension::ID); InModel->AddDynamicExtension(FFolderModelStorageExtension::ID); InModel->AddDynamicExtension(FObjectBindingModelStorageExtension::ID); InModel->AddDynamicExtension(FTrackModelStorageExtension::ID, TrackModelDelegates); InModel->AddDynamicExtension(FTrackRowModelStorageExtension::ID); InModel->AddDynamicExtension(FSectionModelStorageExtension::ID); } ); ObjectBindingContextMenuExtensibilityManager = MakeShareable( new FExtensibilityManager ); AddTrackMenuExtensibilityManager = MakeShareable( new FExtensibilityManager ); ToolBarExtensibilityManager = MakeShareable(new FExtensibilityManager); ActionsMenuExtensibilityManager = MakeShareable(new FExtensibilityManager); SequencerCustomizationManager = MakeShareable(new FSequencerCustomizationManager); } virtual void ShutdownModule() override { if (GIsEditor) { UMovieSceneSignedObject::SetDeferredHandler(nullptr); FSequencerCommands::Unregister(); if (FPropertyEditorModule* EditModulePtr = FModuleManager::Get().GetModulePtr("PropertyEditor")) { EditModulePtr->GetGlobalRowExtensionDelegate().Remove(OnGetGlobalRowExtensionHandle); } FEditorModeRegistry::Get().UnregisterMode(FSequencerEdMode::EM_SequencerMode); } } virtual void RegisterPropertyAnimator(FAnimatedPropertyKey Key) override { PropertyAnimators.Add(Key); } virtual void UnRegisterPropertyAnimator(FAnimatedPropertyKey Key) override { PropertyAnimators.Remove(Key); } virtual bool CanAnimateProperty(FProperty* Property) override { if (PropertyAnimators.Contains(FAnimatedPropertyKey::FromProperty(Property))) { return true; } FObjectPropertyBase* ObjectProperty = CastField(Property); // Check each level of the property hierarchy FFieldClass* PropertyType = Property->GetClass(); while (PropertyType && PropertyType != FProperty::StaticClass()) { FAnimatedPropertyKey Key = FAnimatedPropertyKey::FromPropertyTypeName(PropertyType->GetFName()); // For object properties, check each parent type of the object (ie, so a track that animates UBaseClass ptrs can be used with a UDerivedClass property) UClass* ClassType = (ObjectProperty && ObjectProperty->PropertyClass) ? ObjectProperty->PropertyClass->GetSuperClass() : nullptr; while (ClassType) { Key.ObjectTypeName = ClassType->GetFName(); if (PropertyAnimators.Contains(Key)) { return true; } ClassType = ClassType->GetSuperClass(); } Key.ObjectTypeName = NAME_None; if (PropertyAnimators.Contains(Key)) { return true; } // Look at the property's super class PropertyType = PropertyType->GetSuperClass(); } return false; } virtual TSharedPtr GetObjectBindingContextMenuExtensibilityManager() const override { return ObjectBindingContextMenuExtensibilityManager; } virtual TSharedPtr GetAddTrackMenuExtensibilityManager() const override { return AddTrackMenuExtensibilityManager; } virtual TSharedPtr GetToolBarExtensibilityManager() const override { return ToolBarExtensibilityManager; } virtual TSharedPtr GetActionsMenuExtensibilityManager() const override { return ActionsMenuExtensibilityManager; } virtual TSharedPtr GetSequencerCustomizationManager() const override { return SequencerCustomizationManager; } virtual FDelegateHandle RegisterMovieRenderer(TUniquePtr&& InMovieRenderer) override { FDelegateHandle NewHandle(FDelegateHandle::GenerateNewHandle); MovieRenderers.Add(FMovieRendererEntry{ NewHandle, MoveTemp(InMovieRenderer) }); return NewHandle; } virtual void UnregisterMovieRenderer(FDelegateHandle InDelegateHandle) override { MovieRenderers.RemoveAll([InDelegateHandle](const FMovieRendererEntry& In){ return In.Handle == InDelegateHandle; }); } virtual IMovieRendererInterface* GetMovieRenderer(const FString& InMovieRendererName) override { for (const FMovieRendererEntry& MovieRenderer : MovieRenderers) { if (MovieRenderer.Renderer->GetDisplayName() == InMovieRendererName) { return MovieRenderer.Renderer.Get(); } } return nullptr; } virtual TArray GetMovieRendererNames() override { TArray MovieRendererNames; for (const FMovieRendererEntry& MovieRenderer : MovieRenderers) { MovieRendererNames.Add(MovieRenderer.Renderer->GetDisplayName()); } return MovieRendererNames; } private: TSet PropertyAnimators; /** List of auto-key handler delegates sequencers will execute when they are created */ TArray< FOnCreateTrackEditor > TrackEditorDelegates; /** List of object binding handler delegates sequencers will execute when they are created */ TArray< FOnCreateEditorObjectBinding > EditorObjectBindingDelegates; /** List of track model creators */ TArray TrackModelDelegates; /** Global details row extension delegate; */ FDelegateHandle OnGetGlobalRowExtensionHandle; /** Multicast delegate used to notify others of sequencer initialization params and allow modification. */ FOnPreSequencerInit OnPreSequencerInit; /** Multicast delegate used to notify others of sequencer creations */ FOnSequencerCreated OnSequencerCreated; struct FAnimatedTypeCache { FDelegateHandle FactoryHandle; TArray> AnimatedTypes; }; /** Map of all track editor factories to property types that they have registered to animated */ TArray AnimatedTypeCache; TSharedPtr ObjectBindingContextMenuExtensibilityManager; TSharedPtr AddTrackMenuExtensibilityManager; TSharedPtr ToolBarExtensibilityManager; TSharedPtr ActionsMenuExtensibilityManager; TSharedPtr SequencerCustomizationManager; struct FMovieRendererEntry { FDelegateHandle Handle; TUniquePtr Renderer; }; /** Array of movie renderers */ TArray MovieRenderers; }; IMPLEMENT_MODULE(FSequencerModule, Sequencer); #undef LOCTEXT_NAMESPACE