// Copyright Epic Games, Inc. All Rights Reserved. #include "SequencerNodeTree.h" #include "Filters/SequencerFilterBar.h" #include "MVVM/ViewModels/ViewModelHierarchy.h" #include "MVVM/Selection/Selection.h" #include "MovieSceneBinding.h" #include "GameFramework/Actor.h" #include "MovieScene.h" #include "MVVM/ViewModels/ViewModel.h" #include "MVVM/Extensions/IOutlinerExtension.h" #include "MVVM/Extensions/ICurveEditorTreeItemExtension.h" #include "MVVM/Extensions/IPinnableExtension.h" #include "MVVM/Extensions/ISoloableExtension.h" #include "MVVM/Extensions/IMutableExtension.h" #include "MVVM/ViewModels/SequencerEditorViewModel.h" #include "MVVM/ViewModels/OutlinerViewModel.h" #include "MVVM/ViewModels/CategoryModel.h" #include "MVVM/ViewModels/FolderModel.h" #include "MVVM/ViewModels/ViewModelIterators.h" #include "MVVM/ViewModels/ObjectBindingModel.h" #include "MVVM/ViewModels/OutlinerSpacer.h" #include "MVVM/ViewModels/SectionModel.h" #include "MVVM/ViewModels/SequenceModel.h" #include "MVVM/ViewModels/TrackModel.h" #include "MVVM/CurveEditorIntegrationExtension.h" #include "MVVM/ObjectBindingModelStorageExtension.h" #include "MVVM/SectionModelStorageExtension.h" #include "MVVM/TrackModelStorageExtension.h" #include "IKeyArea.h" #include "ISequencerSection.h" #include "MovieSceneSequence.h" #include "Sequencer.h" #include "MovieSceneFolder.h" #include "ISequencerTrackEditor.h" #include "Widgets/Views/STableRow.h" #include "CurveEditor.h" #include "Channels/MovieSceneChannel.h" #include "ScopedTransaction.h" #include "SequencerUtilities.h" #include "SequencerLog.h" #include "SequencerCommonHelpers.h" #include "SSequencer.h" FSequencerNodeTree::~FSequencerNodeTree() { } FSequencerNodeTree::FSequencerNodeTree(FSequencer& InSequencer) : Sequencer(InSequencer) , bFilterUpdateRequested(false) { } TSharedPtr FSequencerNodeTree::FindObjectBindingNode(const FGuid& BindingID) const { using namespace UE::Sequencer; const FObjectBindingModelStorageExtension* ObjectBindingStorage = RootNode->CastDynamic(); if (ObjectBindingStorage) { TSharedPtr Model = ObjectBindingStorage->FindModelForObjectBinding(BindingID); return Model; } return nullptr; } bool FSequencerNodeTree::UpdateFiltersOnTrackValueChanged() { // If filters are already scheduled for update, we can defer until the next update if (bFilterUpdateRequested) { return false; } if (Sequencer.GetFilterBar()->ShouldUpdateOnTrackValueChanged()) { // UpdateFilters will only run if bFilterUpdateRequested is true bFilterUpdateRequested = true; bool bFiltersUpdated = UpdateFilters(); // If the filter list was modified, set bFilterUpdateRequested to suppress excessive re-filters between tree update bFilterUpdateRequested = bFiltersUpdated; return bFiltersUpdated; } return false; } void FSequencerNodeTree::Update() { using namespace UE::Sequencer; FViewModelHierarchyOperation UpdateOp(RootNode->GetSharedData()); if (!ensure(RootNode)) { return; } TSharedPtr SequenceModel = RootNode->CastThisShared(); check(SequenceModel); UMovieSceneSequence* CurrentSequence = SequenceModel->GetSequence(); check(CurrentSequence); UMovieScene* MovieScene = CurrentSequence->GetMovieScene(); CleanupMuteSolo(MovieScene); // Cache pinned state of nodes, this needs to happen before UpdateFilters() below as some filters will look at the pinned state of child nodes FPinnableExtensionShim::UpdateCachedPinnedState(RootNode); // Re-filter the tree after updating // @todo sequencer: Newly added sections may need to be visible even when there is a filter bFilterUpdateRequested = true; UpdateFilters(); // Sort all nodes const bool bIncludeRootNode = true; for (TSharedPtr SortableChild : RootNode->GetDescendantsOfType(bIncludeRootNode)) { SortableChild->SortChildren(); } // Avoid updating geometry during an undo/redo, as we may have changed the nodes and they won't get updated until next frame. // Any deleted nodes will be present in the hierarchy but garbage. if (!GIsTransacting) { // Update all virtual geometries // This must happen after the sorting IGeometryExtension::UpdateVirtualGeometry(0.f, RootNode); } // Update curve editor tree based on new filtered hierarchy auto CurveEditorIntegration = SequenceModel->CastDynamic(); if (CurveEditorIntegration) { CurveEditorIntegration->UpdateCurveEditor(); } OnUpdatedDelegate.Broadcast(); } UE::Sequencer::TViewModelPtr FindNodeWithPath(UE::Sequencer::TViewModelPtr InNode, const FString& NodePath) { using namespace UE::Sequencer; if (!InNode) { return nullptr; } FString HeadPath, TailPath; const bool bHasDelimiter = NodePath.Split(".", &HeadPath, &TailPath); const FString NodeIdentifier = InNode->GetIdentifier().ToString(); if (bHasDelimiter) { if (NodeIdentifier != HeadPath) { // The node we're looking for is not in this sub-branch. return nullptr; } } else { // NodePath is just a name, so simply check with our node's name. return (NodeIdentifier == NodePath) ? InNode : nullptr; } check(bHasDelimiter && !TailPath.IsEmpty()); for (TViewModelPtr Child : InNode.AsModel()->GetChildrenOfType()) { TViewModelPtr FoundNode = FindNodeWithPath(Child, TailPath); if (FoundNode) { return FoundNode; } } return nullptr; } UE::Sequencer::TViewModelPtr FSequencerNodeTree::GetNodeAtPath(const FString& NodePath) const { using namespace UE::Sequencer; for (const TViewModelPtr& RootChild : RootNode->GetChildrenOfType()) { TViewModelPtr NodeAtPath = FindNodeWithPath(RootChild, NodePath); if (NodeAtPath) { return NodeAtPath; } } return nullptr; } void FSequencerNodeTree::SetRootNode(const UE::Sequencer::FViewModelPtr& InRootNode) { ensureMsgf(!RootNode, TEXT("Re-assinging the root node is currently an undefined behavior")); RootNode = InRootNode; } UE::Sequencer::FViewModelPtr FSequencerNodeTree::GetRootNode() const { return RootNode; } TArray> FSequencerNodeTree::GetRootNodes() const { using namespace UE::Sequencer; TArray> RootNodes; for (const TViewModelPtr& Child : RootNode->GetChildrenOfType()) { RootNodes.Add(Child); } return RootNodes; } void FSequencerNodeTree::ClearCustomSortOrders() { using namespace UE::Sequencer; const bool bIncludeRootNode = true; for (TSharedPtr Child : RootNode->GetDescendants(bIncludeRootNode)) { if (ISortableExtension* SortableExtension = Child->CastThis()) { SortableExtension->SetCustomOrder(-1); } } } void FSequencerNodeTree::SortAllNodesAndDescendants() { using namespace UE::Sequencer; const bool bIncludeRootNode = true; TArray SortableChildren; for (TSharedPtr Child : RootNode->GetDescendants(bIncludeRootNode)) { if (ISortableExtension* SortableExtension = Child->CastThis()) { SortableChildren.Add(SortableExtension); } } for (ISortableExtension* SortableChild : SortableChildren) { SortableChild->SortChildren(); } } void FSequencerNodeTree::SaveExpansionState(const UE::Sequencer::FViewModel& Node, bool bExpanded) { using namespace UE::Sequencer; // @todo Sequencer - This should be moved to the sequence level UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene(); FMovieSceneEditorData& EditorData = MovieScene->GetEditorData(); EditorData.ExpansionStates.Add(IOutlinerExtension::GetPathName(Node), FMovieSceneExpansionState(bExpanded)); } TOptional FSequencerNodeTree::GetSavedExpansionState( const FViewModel& Node ) { using namespace UE::Sequencer; UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene(); FMovieSceneEditorData& EditorData = MovieScene->GetEditorData(); FMovieSceneExpansionState* ExpansionState = EditorData.ExpansionStates.Find(IOutlinerExtension::GetPathName(Node)); return ExpansionState ? ExpansionState->bExpanded : TOptional(); } void FSequencerNodeTree::SavePinnedState(const UE::Sequencer::FViewModel& Node, bool bPinned) { using namespace UE::Sequencer; UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene(); FMovieSceneEditorData& EditorData = MovieScene->GetEditorData(); if (bPinned) { EditorData.PinnedNodes.AddUnique(IOutlinerExtension::GetPathName(Node)); } else { EditorData.PinnedNodes.RemoveSingle(IOutlinerExtension::GetPathName(Node)); } } bool FSequencerNodeTree::GetSavedPinnedState(const UE::Sequencer::FViewModel& Node) const { using namespace UE::Sequencer; UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene(); FMovieSceneEditorData& EditorData = MovieScene->GetEditorData(); bool bPinned = EditorData.PinnedNodes.Contains(IOutlinerExtension::GetPathName(Node)); return bPinned; } bool FSequencerNodeTree::IsNodeFiltered(const TSharedPtr& Node) const { using namespace UE::Sequencer; const TViewModelPtr OutlinerItem = CastViewModel(Node); return OutlinerItem && !OutlinerItem->IsFilteredOut(); } TSharedPtr FSequencerNodeTree::GetSectionModel(const UMovieSceneSection* Section) const { using namespace UE::Sequencer; FSectionModelStorageExtension* SectionStorage = RootNode->CastThis(); if (ensure(SectionStorage)) { return SectionStorage->FindModelForSection(Section); } return nullptr; } bool FSequencerNodeTree::UpdateFilters() { using namespace UE::Sequencer; if (!bFilterUpdateRequested) { return false; } const TSharedPtr FilterBar = Sequencer.GetFilterBar(); if (!FilterBar.IsValid()) { return false; } const FSequencerFilterData& PreviousFilterData = FilterBar->GetFilterData(); const FSequencerFilterData& FilterData = FilterBar->FilterNodes(); bFilteringOnNodeGroups = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene()->GetNodeGroups().HasAnyActiveFilter(); bFilterUpdateRequested = false; // Return whether the new list of FilteredNodes is different than the previous list return (PreviousFilterData.GetDisplayNodeCount() != FilterData.GetDisplayNodeCount() || PreviousFilterData != FilterData); } void FSequencerNodeTree::CleanupMuteSolo(UMovieScene* MovieScene) { // Remove mute/solo markers for any nodes that no longer exist if (!MovieScene->IsReadOnly()) { for (auto It = MovieScene->GetSoloNodes().CreateIterator(); It; ++It) { if (!GetNodeAtPath(*It)) { It.RemoveCurrent(); } } for (auto It = MovieScene->GetMuteNodes().CreateIterator(); It; ++It) { if (!GetNodeAtPath(*It)) { It.RemoveCurrent(); } } } } int32 FSequencerNodeTree::GetTotalDisplayNodeCount() const { return Sequencer.GetFilterBar()->GetFilterData().GetTotalNodeCount(); } int32 FSequencerNodeTree::GetFilteredDisplayNodeCount() const { return Sequencer.GetFilterBar()->GetFilterData().GetDisplayNodeCount(); } void FSequencerNodeTree::SetTextFilterString(const FString& InFilter) { const TSharedRef FilterBar = Sequencer.GetFilterBar(); const FString FilterString = FilterBar->GetTextFilterString(); if (InFilter != FilterString) { bFilterUpdateRequested = true; FilterBar->SetTextFilterString(InFilter); } } void FSequencerNodeTree::NodeGroupsCollectionChanged() { if (Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene()->GetNodeGroups().HasAnyActiveFilter() || bFilteringOnNodeGroups) { RequestFilterUpdate(); } } void FSequencerNodeTree::GetAllNodes(TArray>& OutNodes) const { using namespace UE::Sequencer; const bool bIncludeRootNode = false; for (const FViewModelPtr& It : RootNode->GetDescendants(bIncludeRootNode)) { OutNodes.Add(It.AsModel()); } } void FSequencerNodeTree::GetAllNodes(TArray>& OutNodes) const { using namespace UE::Sequencer; const bool bIncludeRootNode = false; for (const FViewModelPtr& It : RootNode->GetDescendants(bIncludeRootNode)) { OutNodes.Add(It.AsModel().ToSharedRef()); } }