Files
UnrealEngine/Engine/Source/Editor/Sequencer/Private/MVVM/CurveEditorIntegrationExtension.cpp
2025-05-18 13:04:45 +08:00

221 lines
6.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MVVM/CurveEditorIntegrationExtension.h"
#include "MVVM/CurveEditorExtension.h"
#include "MVVM/TrackModelStorageExtension.h"
#include "MVVM/ViewModels/ViewModel.h"
#include "MVVM/SharedViewModelData.h"
#include "MVVM/ViewModelPtr.h"
#include "MVVM/ViewModels/SequenceModel.h"
#include "MVVM/ViewModels/SequencerEditorViewModel.h"
#include "MVVM/ViewModels/ViewModelIterators.h"
#include "MVVM/Extensions/ICurveEditorTreeItemExtension.h"
#include "CurveEditor.h"
#include "SequencerSettings.h"
namespace UE
{
namespace Sequencer
{
FCurveEditorIntegrationExtension::FCurveEditorIntegrationExtension()
{
}
void FCurveEditorIntegrationExtension::OnCreated(TSharedRef<FViewModel> InWeakOwner)
{
ensureMsgf(!WeakOwnerModel.Pin().IsValid(), TEXT("This extension was already created!"));
WeakOwnerModel = InWeakOwner->CastThisShared<FSequenceModel>();
FSimpleMulticastDelegate& HierarchyChanged = InWeakOwner->GetSharedData()->SubscribeToHierarchyChanged(InWeakOwner);
HierarchyChanged.AddSP(this, &FCurveEditorIntegrationExtension::OnHierarchyChanged);
}
void FCurveEditorIntegrationExtension::OnHierarchyChanged()
{
UpdateCurveEditor();
}
FCurveEditorExtension* FCurveEditorIntegrationExtension::GetCurveEditorExtension()
{
if (TSharedPtr<FSequenceModel> OwnerModel = WeakOwnerModel.Pin())
{
if (TSharedPtr<FSequencerEditorViewModel> EditorViewModel = OwnerModel->GetEditor())
{
return EditorViewModel->CastDynamic<FCurveEditorExtension>();
}
}
return nullptr;
}
void FCurveEditorIntegrationExtension::UpdateCurveEditor()
{
TSharedPtr<FSequenceModel> OwnerModel = WeakOwnerModel.Pin();
if (!OwnerModel)
{
return;
}
FCurveEditorExtension* CurveEditorExtension = GetCurveEditorExtension();
if (!CurveEditorExtension)
{
return;
}
TSharedPtr<FCurveEditor> CurveEditor = CurveEditorExtension->GetCurveEditor();
if (!CurveEditor)
{
return;
}
const bool bUseFilteredOut = OwnerModel->GetSequencer()->GetSequencerSettings()->GetLinkFiltersWithCurveEditor();
FCurveEditorTree* CurveEditorTree = CurveEditor->GetTree();
// Guard against multiple broadcasts here and defer them until the end of this function
FScopedCurveEditorTreeEventGuard ScopedEventGuard = CurveEditorTree->ScopedEventGuard();
// Remove any stale tree items
for (auto It = ViewModelToTreeItemIDMap.CreateIterator(); It; ++It)
{
bool bIsRelevant = false;
const FCurveEditorTreeItemID ItemID(It.Value());
TViewModelPtr<ICurveEditorTreeItemExtension> ViewModel = CastViewModel<ICurveEditorTreeItemExtension>(It.Key().Pin());
if (ViewModel)
{
TViewModelPtr<IOutlinerExtension> OutlinerModel = ViewModel.ImplicitCast();
const bool bIsVisible = bUseFilteredOut ? OutlinerModel && !OutlinerModel->IsFilteredOut() : true;
const FCurveEditorTreeItem* TreeItem = CurveEditorTree->FindItem(ItemID);
FCurveEditorTreeItemID ParentID;
if (auto Parent = ViewModel.AsModel()->FindAncestorOfType<ICurveEditorTreeItemExtension>())
{
ParentID = ViewModelToTreeItemIDMap.FindRef(Parent.AsModel());
}
bIsRelevant = bIsVisible && TreeItem && TreeItem->GetParentID() == ParentID;
}
if (!bIsRelevant)
{
if (ViewModel)
{
ViewModel->OnRemovedFromCurveEditor(CurveEditor);
}
CurveEditor->RemoveTreeItem(ItemID);
It.RemoveCurrent();
}
}
// Do a second pass to remove any items that were removed recursively above
for (auto It = ViewModelToTreeItemIDMap.CreateIterator(); It; ++It)
{
if (CurveEditorTree->FindItem(It->Value) == nullptr)
{
It.RemoveCurrent();
}
}
// Iterate all non-filtered out outliners and check for curve editor tree extensions
const bool bIncludeRootNode = false;
bool bItemsAdded = false;
for (TParentFirstChildIterator<IOutlinerExtension> It(OwnerModel, bIncludeRootNode); It; ++It)
{
if (bUseFilteredOut && It->IsFilteredOut())
{
It.IgnoreCurrentChildren();
continue;
}
if (TViewModelPtr<ICurveEditorTreeItemExtension> ChildViewModel = (*It).ImplicitCast())
{
const FCurveEditorTreeItemID ItemID = ViewModelToTreeItemIDMap.FindRef(ChildViewModel.AsModel());
if (!ItemID.IsValid())
{
bItemsAdded = true;
AddToCurveEditor(ChildViewModel, CurveEditor);
}
}
}
// If new items have been added, synchronize selection after the view models have been added to the curve editor. Synchronization depends on curve model tree item IDs
if (bItemsAdded)
{
CurveEditorExtension->RequestSyncSelection();
}
}
FCurveEditorTreeItemID FCurveEditorIntegrationExtension::AddToCurveEditor(TViewModelPtr<ICurveEditorTreeItemExtension> InViewModel, TSharedPtr<FCurveEditor> InCurveEditor)
{
// If the view model doesn't want to be in the curve editor, bail out
// Note that this means we will create curve editor items for each parent in the hierarchy up
// until the first parent that doesn't implement ICurveEditorTreeItemExtension
// That is: we don't create "dummy" entries when there's a "gap" in the hierarchy
if (!InViewModel)
{
return FCurveEditorTreeItemID::Invalid();
}
// Check if we already have a valid curve editor ID
if (FCurveEditorTreeItemID* Existing = ViewModelToTreeItemIDMap.Find(InViewModel.AsModel()))
{
if (InCurveEditor->GetTree()->FindItem(*Existing) != nullptr)
{
return *Existing;
}
}
// Recursively create any needed parent curve editor items
TViewModelPtr<ICurveEditorTreeItemExtension> Parent = InViewModel.AsModel()->CastParent<ICurveEditorTreeItemExtension>();
FCurveEditorTreeItemID ParentID = Parent ?
AddToCurveEditor(Parent, InCurveEditor) :
FCurveEditorTreeItemID::Invalid();
// Create the new curve editor item
FCurveEditorTreeItem* NewItem = InCurveEditor->AddTreeItem(ParentID);
TSharedPtr<ICurveEditorTreeItem> CurveEditorTreeItem = InViewModel->GetCurveEditorTreeItem();
NewItem->SetWeakItem(CurveEditorTreeItem);
TOptional<FString> UniquePathName = InViewModel->GetUniquePathName();
NewItem->SetUniquePathName(UniquePathName);
// Register the new ID in our map and notify the view model
ViewModelToTreeItemIDMap.Add(InViewModel.AsModel(), NewItem->GetID());
InViewModel->OnAddedToCurveEditor(NewItem->GetID(), InCurveEditor);
return NewItem->GetID();
}
void FCurveEditorIntegrationExtension::ResetCurveEditor()
{
FCurveEditorExtension* CurveEditorExtension = GetCurveEditorExtension();
if (!ensure(CurveEditorExtension))
{
return;
}
TSharedPtr<FCurveEditor> CurveEditor = CurveEditorExtension->GetCurveEditor();
if (!ensure(CurveEditor))
{
return;
}
for (TPair<TWeakPtr<FViewModel>, FCurveEditorTreeItemID> Pair : ViewModelToTreeItemIDMap)
{
FCurveEditorTreeItemID ItemID(Pair.Value);
TViewModelPtr<ICurveEditorTreeItemExtension> ViewModel = CastViewModel<ICurveEditorTreeItemExtension>(Pair.Key.Pin());
if (ViewModel)
{
ViewModel->OnRemovedFromCurveEditor(CurveEditor);
}
CurveEditor->RemoveTreeItem(ItemID);
}
ViewModelToTreeItemIDMap.Reset();
}
} // namespace Sequencer
} // namespace UE