// Copyright Epic Games, Inc. All Rights Reserved. #include "MVVM/ViewModels/ChannelModel.h" #include "Channels/IMovieSceneChannelOverrideProvider.h" #include "Channels/MovieSceneChannel.h" #include "Channels/MovieSceneChannelHandle.h" #include "Channels/MovieSceneSectionChannelOverrideRegistry.h" #include "Channels/IMovieSceneChannelOwner.h" #include "CurveModel.h" #include "DetailsViewArgs.h" #include "HAL/PlatformCrt.h" #include "IKeyArea.h" #include "ISequencerChannelInterface.h" #include "ISequencerSection.h" #include "MVVM/Extensions/ITrackExtension.h" #include "MVVM/ViewModels/ObjectBindingModel.h" #include "MVVM/ViewModels/SectionModel.h" #include "MVVM/ViewModels/SequenceModel.h" #include "MVVM/ViewModels/SequencerEditorViewModel.h" #include "MVVM/ViewModels/SequencerModelUtils.h" #include "MVVM/ViewModels/ViewModelIterators.h" #include "MVVM/ViewModels/OutlinerViewModel.h" #include "MVVM/ViewModels/TrackModel.h" #include "MVVM/ViewModels/OutlinerColumns/OutlinerColumnTypes.h" #include "MVVM/Selection/Selection.h" #include "MVVM/Views/SOutlinerView.h" #include "MVVM/Views/SOutlinerItemViewBase.h" #include "MVVM/Views/SSequencerKeyNavigationButtons.h" #include "MVVM/Views/SOutlinerTrackColorPicker.h" #include "MVVM/Views/STrackLane.h" #include "Math/UnrealMathUtility.h" #include "Misc/AssertionMacros.h" #include "Modules/ModuleManager.h" #include "MovieSceneTrack.h" #include "MVVM/Views/SChannelView.h" #include "PropertyEditorModule.h" #include "SKeyAreaEditorSwitcher.h" #include "SequencerCoreFwd.h" #include "SequencerSectionPainter.h" #include "SequencerSettings.h" #include "SlotBase.h" #include "Styling/AppStyle.h" #include "Templates/TypeHash.h" #include "Templates/UnrealTemplate.h" #include "Types/SlateEnums.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SBoxPanel.h" #include "Algo/AnyOf.h" class ISequencer; class SWidget; #define LOCTEXT_NAMESPACE "SequencerChannelModel" namespace UE::Sequencer { struct FDynamicChannelMuteExtension : IDynamicExtension, IMutableExtension { TWeakObjectPtr<> WeakOwner; FName ChannelName; using Implements = TImplements; UE_SEQUENCER_DECLARE_VIEW_MODEL_TYPE_ID(FDynamicChannelMuteExtension) FDynamicChannelMuteExtension(TWeakObjectPtr<> InWeakOwner, FName InChannelName) : WeakOwner(InWeakOwner) , ChannelName(InChannelName) {} bool IsMuted() const override { IMovieSceneChannelOwner* ChannelOwner = Cast(WeakOwner.Get()); return ChannelOwner && ChannelOwner->IsMuted(ChannelName); } void SetIsMuted(bool bIsMuted) override { IMovieSceneChannelOwner* ChannelOwner = Cast(WeakOwner.Get()); if (ChannelOwner) { ChannelOwner->SetIsMuted(ChannelName, bIsMuted); } } }; UE_SEQUENCER_DEFINE_VIEW_MODEL_TYPE_ID(FDynamicChannelMuteExtension); struct FDynamicChannelGroupMuteExtension : IDynamicExtension, IMutableExtension { TWeakViewModelPtr WeakChannelGroup; using Implements = TImplements; UE_SEQUENCER_DECLARE_VIEW_MODEL_TYPE_ID(FDynamicChannelGroupMuteExtension) FDynamicChannelGroupMuteExtension(TWeakViewModelPtr InWeakChannelGroup) : WeakChannelGroup(InWeakChannelGroup) {} bool IsInheritable() const override { return false; } bool IsMuted() const override { TViewModelPtr ChannelGroup = WeakChannelGroup.Pin(); if (!ChannelGroup) { return false; } for (const TWeakViewModelPtr& WeakChannel : ChannelGroup->GetChannels()) { TViewModelPtr Mutable = WeakChannel.ImplicitPin(); if (Mutable && Mutable->IsMuted()) { return true; } } return false; } void SetIsMuted(bool bIsMuted) override { TViewModelPtr ChannelGroup = WeakChannelGroup.Pin(); if (ChannelGroup) { for (const TWeakViewModelPtr& WeakChannel : ChannelGroup->GetChannels()) { TViewModelPtr Mutable = WeakChannel.ImplicitPin(); if (Mutable) { Mutable->SetIsMuted(bIsMuted); } } } } }; UE_SEQUENCER_DEFINE_VIEW_MODEL_TYPE_ID(FDynamicChannelGroupMuteExtension); FChannelModel::FChannelModel(FName InChannelName, TWeakPtr InSection, FMovieSceneChannelHandle InChannel) : CurveModelID(MakePimpl(FCurveModelID::Unique())) , KeyArea(MakeShared(InSection, InChannel, *CurveModelID)) , ChannelName(InChannelName) { } FChannelModel::~FChannelModel() { } bool FChannelModel::IsAnimated() const { if (KeyArea) { FMovieSceneChannel* Channel = KeyArea->ResolveChannel(); return Channel && Channel->GetNumKeys() > 0; } return false; } void FChannelModel::Initialize(TWeakPtr InSection, FMovieSceneChannelHandle InChannel) { if (!KeyArea) { KeyArea = MakeShared(InSection, InChannel, *CurveModelID); } else { KeyArea->Reinitialize(InSection, InChannel); } const FMovieSceneChannelMetaData* ChannelMetaData = InChannel.GetMetaData(); IMovieSceneChannelOwner* ChannelOwner = Cast(KeyArea->GetOwningObject()); if (ChannelMetaData && ChannelOwner && ChannelOwner->GetCapabilities(KeyArea->GetName()).bSupportsMute) { AddDynamicExtension(KeyArea->GetOwningObject(), ChannelMetaData->Name); } else { RemoveDynamicExtension(); } } FMovieSceneChannel* FChannelModel::GetChannel() const { check(KeyArea); return KeyArea->ResolveChannel(); } UMovieSceneSection* FChannelModel::GetSection() const { return KeyArea->GetOwningSection(); } UObject* FChannelModel::GetOwningObject() const { return KeyArea->GetOwningObject(); } FOutlinerSizing FChannelModel::GetDesiredSizing() const { FOutlinerSizing Sizing(15.f); if (KeyArea->ShouldShowCurve()) { TViewModelPtr Sequence = FindAncestorOfType(); if (Sequence) { Sizing.Height = Sequence->GetSequencer()->GetSequencerSettings()->GetKeyAreaHeightWithCurves(); Sizing.Flags |= EOutlinerSizingFlags::CustomHeight; } } return Sizing; } TSharedPtr FChannelModel::CreateTrackLaneView(const FCreateTrackLaneViewParams& InParams) { ISequencerChannelInterface* EditorInterface = KeyArea->FindChannelEditorInterface(); if (EditorInterface) { TSharedPtr CustomWidget = EditorInterface->CreateChannelView_Raw(KeyArea->GetChannel(), SharedThis(this), InParams); if (CustomWidget) { return CustomWidget; } } return SNew(SChannelView, SharedThis(this), InParams.OwningTrackLane->GetTrackAreaView()) .KeyBarColor(this, &FChannelModel::GetKeyBarColor); } FTrackLaneVirtualAlignment FChannelModel::ArrangeVirtualTrackLaneView() const { TSharedPtr Section = FindAncestorOfType(); if (Section) { return FTrackLaneVirtualAlignment::Proportional(Section->GetRange(), 1.f); } return FTrackLaneVirtualAlignment::Proportional(TRange(), 1.f); } void FChannelModel::OnRecycle() { KeyArea = nullptr; } bool FChannelModel::UpdateCachedKeys(TSharedPtr& OutCachedKeys) const { struct FSequencerCachedKeys : FCachedKeys { FSequencerCachedKeys(const FChannelModel* InChannel) { Update(InChannel); } bool Update(const FChannelModel* InChannel) { UMovieSceneSection* Section = InChannel->GetSection(); if (!Section || !CachedSignature.IsValid() || Section->GetSignature() != CachedSignature) { CachedSignature = Section && InChannel->GetKeyArea() && InChannel->GetKeyArea()->ResolveChannel() ? Section->GetSignature() : FGuid(); KeyTimes.Reset(); KeyHandles.Reset(); TArray KeyFrames; InChannel->GetKeyArea()->GetKeyInfo(&KeyHandles, &KeyFrames); KeyTimes.SetNumUninitialized(KeyFrames.Num()); for (int32 Index = 0; Index < KeyFrames.Num(); ++Index) { KeyTimes[Index] = KeyFrames[Index]; } return true; } return false; } /** The guid with which the above array was generated */ FGuid CachedSignature; }; if (OutCachedKeys) { return StaticCastSharedPtr(OutCachedKeys)->Update(this); } else { OutCachedKeys = MakeShared(this); return true; } } bool FChannelModel::GetFixedExtents(double& OutFixedMin, double& OutFixedMax) const { TViewModelPtr SequenceModel = FindAncestorOfType(); if (!SequenceModel) { return false; } FString KeyAreaName = KeyArea->GetName().ToString(); if (SequenceModel->GetSequencer()->GetSequencerSettings()->HasKeyAreaCurveExtents(KeyAreaName)) { SequenceModel->GetSequencer()->GetSequencerSettings()->GetKeyAreaCurveExtents(KeyAreaName, OutFixedMin, OutFixedMax); return true; } return false; } int32 FChannelModel::CustomPaint(const FSequencerChannelPaintArgs& PaintArgs, int32 LayerId) const { return KeyArea->DrawExtra(PaintArgs, LayerId); } void FChannelModel::DrawKeys(TArrayView InKeyHandles, TArrayView OutKeyDrawParams) { KeyArea->DrawKeys(InKeyHandles, OutKeyDrawParams); } TUniquePtr FChannelModel::CreateCurveModel() { if (TViewModelPtr SequenceModel = FindAncestorOfType()) { return KeyArea->CreateCurveEditorModel(SequenceModel->GetSequencer().ToSharedRef()); } return nullptr; } FLinearColor FChannelModel::GetKeyBarColor() const { if (TViewModelPtr SequenceModel = FindAncestorOfType()) { if (SequenceModel->GetSequencer()->GetSequencerSettings()->GetShowChannelColors()) { TOptional ChannelColor = KeyArea->GetColor(); if (ChannelColor) { return ChannelColor.GetValue(); } } } TViewModelPtr Track = FindAncestorOfType(); UMovieSceneTrack* TrackObject = Track ? Track->GetTrack() : nullptr; if (TrackObject) { FLinearColor Tint = FSequencerSectionPainter::BlendColor(TrackObject->GetColorTint()).LinearRGBToHSV(); // If this is a top level chanel, draw using the fill color FViewModelPtr OutlinerItem = GetLinkedOutlinerItem(); if (OutlinerItem && !OutlinerItem->IsA()) { Tint.G *= .5f; Tint.B = FMath::Max(.03f, Tint.B*.1f); } return Tint.HSVToLinearRGB().CopyWithNewOpacity(1.f); } return FColor(160, 160, 160); } FChannelGroupModel::FChannelGroupModel(FName InChannelName, const FText& InDisplayText) : ChannelsSerialNumber(0) , ChannelName(InChannelName) , DisplayText(InDisplayText) { } FChannelGroupModel::FChannelGroupModel(FName InChannelName, const FText& InDisplayText, const FText& InTooltipText) : ChannelsSerialNumber(0) , ChannelName(InChannelName) , DisplayText(InDisplayText) , GetTooltipTextDelegate(FGetMovieSceneTooltipText::CreateLambda([InTooltipText](...) {return InTooltipText; })) { } FChannelGroupModel::FChannelGroupModel(FName InChannelName, const FText& InDisplayText, FGetMovieSceneTooltipText InGetTooltipTextDelegate) : ChannelsSerialNumber(0) , ChannelName(InChannelName) , DisplayText(InDisplayText) , GetTooltipTextDelegate(InGetTooltipTextDelegate) { } FChannelGroupModel::~FChannelGroupModel() { } bool FChannelGroupModel::IsAnimated() const { for (TWeakViewModelPtr WeakChannel : Channels) { if (TSharedPtr Channel = WeakChannel.Pin()) { if (Channel->IsAnimated()) { return true; } } } return false; } void FChannelGroupModel::AddChannel(TWeakViewModelPtr InChannel) { if (!Channels.Contains(InChannel)) { Channels.Add(InChannel); ++ChannelsSerialNumber; } } uint32 FChannelGroupModel::GetChannelsSerialNumber() const { const_cast(this)->CleanupChannels(); return ChannelsSerialNumber; } void FChannelGroupModel::OnChannelOverridesChanged() { ++ChannelsSerialNumber; } void FChannelGroupModel::CleanupChannels() { const int32 NumRemoved = Channels.RemoveAll([](TWeakViewModelPtr Item) { return !Item.Pin().IsValid(); }); if (NumRemoved > 0) { ++ChannelsSerialNumber; const bool bShouldBeMutable = Algo::AnyOf(Channels, [](TWeakViewModelPtr In) { TViewModelPtr Mutable = In.ImplicitPin(); return Mutable.IsValid(); } ); const bool bIsMutable = CastDynamic() != nullptr; if (!bShouldBeMutable && bIsMutable) { RemoveDynamicExtension(); } } } FText FChannelGroupModel::GetTooltipText() const { if (GetTooltipTextDelegate.IsBound()) { if (TViewModelPtr SequenceModel = FindAncestorOfType()) { if (TSharedPtr SequencerModel = SequenceModel->GetEditor()) { FMovieSceneSequenceID SequenceID = SequenceModel->GetSequenceID(); IMovieScenePlayer* Player = SequencerModel->GetSequencer().Get(); if (Player) { if (TViewModelPtr ObjectBindingModel = FindAncestorOfType()) { FGuid ObjectBindingID = ObjectBindingModel->GetObjectGuid(); return GetTooltipTextDelegate.Execute(Player, ObjectBindingID, SequenceID); } } } } } return FText(); } TArrayView> FChannelGroupModel::GetChannels() const { return Channels; } TSharedPtr FChannelGroupModel::GetKeyArea(TSharedPtr InOwnerSection) const { return GetKeyArea(InOwnerSection->GetSection()); } TSharedPtr FChannelGroupModel::GetKeyArea(const UMovieSceneSection* InOwnerSection) const { TSharedPtr Channel = GetChannel(InOwnerSection); return Channel ? Channel->GetKeyArea() : nullptr; } TSharedPtr FChannelGroupModel::GetChannel(int32 Index) const { return Channels[Index].Pin(); } TSharedPtr FChannelGroupModel::GetChannel(TSharedPtr InOwnerSection) const { return GetChannel(InOwnerSection->GetSection()); } TSharedPtr FChannelGroupModel::GetChannel(const UMovieSceneSection* InOwnerSection) const { for (TWeakViewModelPtr WeakChannel : Channels) { if (TSharedPtr Channel = WeakChannel.Pin()) { if (Channel->GetSection() == InOwnerSection) { return Channel; } } } return nullptr; } TArray> FChannelGroupModel::GetAllKeyAreas() const { TArray> KeyAreas; KeyAreas.Reserve(Channels.Num()); for (TWeakViewModelPtr WeakChannel : Channels) { if (TSharedPtr Channel = WeakChannel.Pin()) { if (Channel->GetKeyArea()) { KeyAreas.Add(Channel->GetKeyArea().ToSharedRef()); } } } return KeyAreas; } FTrackAreaParameters FChannelGroupModel::GetTrackAreaParameters() const { FTrackAreaParameters Parameters; Parameters.LaneType = ETrackAreaLaneType::Inline; return Parameters; } FViewModelVariantIterator FChannelGroupModel::GetTrackAreaModelList() const { return &Channels; } void FChannelGroupModel::OnRecycle() { Channels.Empty(); } void FChannelGroupModel::CreateCurveModels(TArray>& OutCurveModels) { TSharedPtr SequenceModel = FindAncestorOfType(); if (!SequenceModel) { return; } TSharedPtr Sequencer = SequenceModel->GetSequencer(); if (!Sequencer) { return; } for (TWeakViewModelPtr WeakChannel : GetChannels()) { if (TSharedPtr Channel = WeakChannel.Pin()) { TUniquePtr NewCurve = Channel->GetKeyArea()->CreateCurveEditorModel(Sequencer.ToSharedRef()); if (NewCurve.IsValid()) { OutCurveModels.Add(MoveTemp(NewCurve)); } } } } TOptional FChannelGroupModel::GetUniquePathName() const { TStringBuilder<256> StringBuilder; IOutlinerExtension::GetPathName(*this, StringBuilder); FString PathName(StringBuilder.ToString()); TOptional FullPathName = PathName; return FullPathName; } bool FChannelGroupModel::HasCurves() const { for (const TSharedRef& KeyArea : GetAllKeyAreas()) { const ISequencerChannelInterface* EditorInterface = KeyArea->FindChannelEditorInterface(); if (EditorInterface && EditorInterface->SupportsCurveEditorModels_Raw(KeyArea->GetChannel())) { return true; } } return false; } void FChannelGroupModel::BuildChannelOverrideMenu(FMenuBuilder& MenuBuilder) { TSharedPtr SequenceModel = FindAncestorOfType(); FChannelGroupOverrideHelper::BuildChannelOverrideMenu(MenuBuilder, SequenceModel); } bool FChannelGroupOverrideHelper::GetChannelsFromOutlinerSelection(TSharedPtr SequenceModel, TArray>& OutChannelModels) { // Sanity checks. if (!SequenceModel) { return false; } TSharedPtr Sequencer = SequenceModel->GetSequencer(); if (!Sequencer) { return false; } // The selected items could either be channel groups, or tracks/track-rows with top-level channel groups that // don't explicitly show up in the outliner (they are view-models that live under the track or row, but the // user interface shows them as the track or row itself). for (FViewModelPtr ViewModel : Sequencer->GetViewModel()->GetSelection()->Outliner) { if (TViewModelPtr ChannelGroup = ViewModel.ImplicitCast()) { const int32 NumChannels = ChannelGroup->GetChannels().Num(); for (int32 ChannelIndex = 0; ChannelIndex < NumChannels; ++ChannelIndex) { if (TSharedPtr Channel = ChannelGroup->GetChannel(ChannelIndex)) { OutChannelModels.Add(Channel); } } } else { const EViewModelListType ListType = FTrackModel::GetTopLevelChannelGroupType(); TOptional TopLevelChannels = ViewModel->FindChildList(ListType); if (TopLevelChannels.IsSet()) { for (auto It(TopLevelChannels->IterateSubList()); It; ++It) { for (TWeakViewModelPtr WeakChannel : It->GetChannels()) { if (TViewModelPtr Channel = WeakChannel.Pin()) { OutChannelModels.Add(Channel); } } } } } } return true; } void FChannelGroupOverrideHelper::BuildChannelOverrideMenu(FMenuBuilder& MenuBuilder, TSharedPtr SequenceModel) { TArray> Channels; if (!GetChannelsFromOutlinerSelection(SequenceModel, Channels)) { return; } // Gather up information: // - Candidates for overriding each of the channel types found in each group. // - Already overriden channels. int32 NumOverridableChannels = 0; TMap> ExistingOverrides; TArray AllCandidateOverrides; for (TViewModelPtr Channel : Channels) { UMovieSceneSection* Section = Channel->GetSection(); if (!Section) { continue; } IMovieSceneChannelOverrideProvider* OverrideProvider = Cast(Section); if (!OverrideProvider) { continue; } ++NumOverridableChannels; UMovieSceneSectionChannelOverrideRegistry* OverrideRegistry = OverrideProvider->GetChannelOverrideRegistry(false); UE::MovieScene::FChannelOverrideProviderTraitsHandle OverrideTraits = OverrideProvider->GetChannelOverrideProviderTraits(); UMovieSceneChannelOverrideContainer* OverrideContainer = OverrideRegistry ? OverrideRegistry->GetChannel(Channel->GetChannelName()) : nullptr; if (OverrideContainer) { ExistingOverrides.FindOrAdd(OverrideContainer->GetClass()).Add(OverrideContainer); } const FName DefaultChannelTypeName = OverrideTraits->GetDefaultChannelTypeName(Channel->GetChannelName()); if (!DefaultChannelTypeName.IsNone()) { UMovieSceneChannelOverrideContainer::FOverrideCandidates OverrideCandidates; UMovieSceneChannelOverrideContainer::GetOverrideCandidates(DefaultChannelTypeName, OverrideCandidates); AllCandidateOverrides.Add(MoveTemp(OverrideCandidates)); } } // Add menu entries for overriding channels, with only types that are common among all sections. TMap, int32> CandidateCounts; for (const UMovieSceneChannelOverrideContainer::FOverrideCandidates& CandidateOverrides : AllCandidateOverrides) { for (TSubclassOf CandidateOverride : CandidateOverrides) { ++CandidateCounts.FindOrAdd(CandidateOverride); } } UMovieSceneChannelOverrideContainer::FOverrideCandidates CommonCandidateOverrides; for (const TTuple, int32>& CandidateCount : CandidateCounts) { if (CandidateCount.Value == NumOverridableChannels) { CommonCandidateOverrides.Add(CandidateCount.Key); } } if (CommonCandidateOverrides.Num() > 0 || ExistingOverrides.Num() > 0) { MenuBuilder.BeginSection("ChannelOverrides", LOCTEXT("ChannelGroupOverrideMenuSectionName", "Channel Overrides")); if (CommonCandidateOverrides.Num() == 1) { BuildChannelOverrideMenu(MenuBuilder, SequenceModel, CommonCandidateOverrides); } else if (CommonCandidateOverrides.Num() > 1) { MenuBuilder.AddSubMenu( LOCTEXT("OverrideChannelChoiceLabel", "Override with..."), LOCTEXT("OverrideChannelChoiceTooltip", "Overrides all channels with the specified channel"), FNewMenuDelegate::CreateStatic(&FChannelGroupOverrideHelper::BuildChannelOverrideMenu, SequenceModel, CommonCandidateOverrides)); } if (ExistingOverrides.Num() > 0) { MenuBuilder.AddMenuEntry( LOCTEXT("RemoveOverrideChannelLabel", "Remove Override"), LOCTEXT("RemoveOverrideChannelTooltip", "Removes the channel override and revert back to the default channel type"), FSlateIcon(), FUIAction( FExecuteAction::CreateStatic(&FChannelGroupOverrideHelper::RemoveChannelOverrides, SequenceModel), FCanExecuteAction())); } MenuBuilder.EndSection(); } } void FChannelGroupOverrideHelper::BuildChannelOverrideMenu(FMenuBuilder& MenuBuilder, TSharedPtr SequenceModel, UMovieSceneChannelOverrideContainer::FOverrideCandidates OverrideCandidates) { for (TSubclassOf OverrideCandidate : OverrideCandidates) { MenuBuilder.AddMenuEntry( FText::Format(LOCTEXT("OverrideChannelLabel", "Override with {0}"), OverrideCandidate->GetDisplayNameText()), FText::Format(LOCTEXT("OverrideChannelTooltip", "Overrides all channels with {0}"), OverrideCandidate->GetDisplayNameText()), FSlateIcon(), FUIAction( FExecuteAction::CreateStatic(&FChannelGroupOverrideHelper::OverrideChannels, SequenceModel, OverrideCandidate), FCanExecuteAction())); } } void FChannelGroupOverrideHelper::OverrideChannels(TSharedPtr SequenceModel, TSubclassOf OverrideClass) { TArray> Channels; if (!GetChannelsFromOutlinerSelection(SequenceModel, Channels)) { return; } bool bAnyChannelsChanged = false; TSet> ChangedChannelGroups; FScopedTransaction Transaction(LOCTEXT("OverrideChannels", "Override Channels")); for (TViewModelPtr Channel : Channels) { UMovieSceneSection* Section = Channel->GetSection(); IMovieSceneChannelOverrideProvider* OverrideProvider = Cast(Section); if (!OverrideProvider) { continue; } if (!Section->TryModify()) { continue; } UMovieSceneSectionChannelOverrideRegistry* OverrideRegistry = OverrideProvider->GetChannelOverrideRegistry(true); check(OverrideRegistry); UMovieSceneChannelOverrideContainer* OverrideChannelContainer = NewObject( OverrideRegistry, OverrideClass, NAME_None, RF_Transactional); OverrideChannelContainer->InitializeOverride(Channel->GetChannel()); OverrideRegistry->AddChannel(Channel->GetChannelName(), OverrideChannelContainer); OverrideProvider->OnChannelOverridesChanged(); bAnyChannelsChanged = true; if (TSharedPtr ParentGroupModel = Channel->CastParent()) { ChangedChannelGroups.Add(ParentGroupModel); } } for (TSharedPtr ChangedChannelGroup : ChangedChannelGroups) { ChangedChannelGroup->OnChannelOverridesChanged(); } if (!bAnyChannelsChanged) { Transaction.Cancel(); } SequenceModel->GetSequencer()->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemsChanged); } void FChannelGroupOverrideHelper::RemoveChannelOverrides(TSharedPtr SequenceModel) { TArray> Channels; if (!GetChannelsFromOutlinerSelection(SequenceModel, Channels)) { return; } bool bAnyChannelsChanged = false; TSet> ChangedChannelGroups; FScopedTransaction Transaction(LOCTEXT("RemoveChannelOverrides", "Remove Override Channels")); for (TViewModelPtr Channel : Channels) { UMovieSceneSection* Section = Channel->GetSection(); IMovieSceneChannelOverrideProvider* OverrideProvider = Cast(Section); if (!OverrideProvider) { continue; } UMovieSceneSectionChannelOverrideRegistry* OverrideRegistry = OverrideProvider->GetChannelOverrideRegistry(false); if (!OverrideRegistry) { continue; } if (!Section->TryModify()) { continue; } OverrideRegistry->RemoveChannel(Channel->GetChannelName()); OverrideProvider->OnChannelOverridesChanged(); bAnyChannelsChanged = true; if (TSharedPtr ParentGroupModel = Channel->CastParent()) { ChangedChannelGroups.Add(ParentGroupModel); } } for (TSharedPtr ChangedChannelGroup : ChangedChannelGroups) { ChangedChannelGroup->OnChannelOverridesChanged(); } if (!bAnyChannelsChanged) { Transaction.Cancel(); } SequenceModel->GetSequencer()->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemsChanged); } FChannelGroupOutlinerModel::FChannelGroupOutlinerModel(FName InChannelName, const FText& InDisplayText, FGetMovieSceneTooltipText InGetTooltipTextDelegate) : TOutlinerModelMixin(InChannelName, InDisplayText, InGetTooltipTextDelegate) { SetIdentifier(InChannelName); } FChannelGroupOutlinerModel::~FChannelGroupOutlinerModel() {} void FChannelGroupOutlinerModel::OnUpdated() { WeakCommonChannelModel = nullptr; // If all channels are the same type, assign the common channel // model for edit interactions and context menus { TViewModelPtr CommonChannel; for (TWeakViewModelPtr WeakChannel : Channels) { if (TViewModelPtr Channel = WeakChannel.Pin()) { if (!CommonChannel) { CommonChannel = Channel; } else if (CommonChannel->GetTypeTable().GetTypeID() != Channel->GetTypeTable().GetTypeID()) { CommonChannel = nullptr; break; } } } WeakCommonChannelModel = CommonChannel; } const bool bShouldBeMutable = Algo::AnyOf(Channels, [](TWeakViewModelPtr In) { TViewModelPtr Mutable = In.ImplicitPin(); return Mutable.IsValid(); } ); const bool bIsMutable = CastDynamic() != nullptr; if (bShouldBeMutable && !bIsMutable) { AddDynamicExtension(SharedThis(this)); } else if (!bShouldBeMutable && bIsMutable) { RemoveDynamicExtension(); } } FOutlinerSizing FChannelGroupOutlinerModel::RecomputeSizing() { FOutlinerSizing MaxSizing; for (TWeakViewModelPtr WeakChannel : Channels) { if (TSharedPtr Channel = WeakChannel.Pin()) { FOutlinerSizing Desired = Channel->GetDesiredSizing(); MaxSizing.Accumulate(Desired); } } if (ComputedSizing != MaxSizing) { ComputedSizing = MaxSizing; for (TWeakViewModelPtr WeakChannel : Channels) { if (TSharedPtr Channel = WeakChannel.Pin()) { Channel->SetComputedSizing(MaxSizing); } } } return MaxSizing; } FOutlinerSizing FChannelGroupOutlinerModel::GetOutlinerSizing() const { if (EnumHasAnyFlags(ComputedSizing.Flags, EOutlinerSizingFlags::DynamicSizing)) { const_cast(this)->RecomputeSizing(); } FOutlinerSizing FinalSizing = ComputedSizing; if (!EnumHasAnyFlags(ComputedSizing.Flags, EOutlinerSizingFlags::CustomHeight)) { FViewDensityInfo Density = GetEditor()->GetViewDensity(); FinalSizing.Height = Density.UniformHeight.Get(FinalSizing.Height); } return FinalSizing; } TSharedPtr FChannelGroupOutlinerModel::CreateOutlinerViewForColumn(const FCreateOutlinerViewParams& InParams, const FName& InColumnName) { TViewModelPtr Editor = InParams.Editor->CastThisShared(); if (!Editor) { return SNullWidget::NullWidget; } // Ask a common channel to populate the outliner column view first if (TViewModelPtr CommonChannel = WeakCommonChannelModel.Pin()) { TSharedPtr Widget = CommonChannel->CreateOutlinerViewForColumn(InParams, InColumnName); if (Widget) { return Widget; } } if (InColumnName == FCommonOutlinerNames::Label) { return SNew(SOutlinerItemViewBase, SharedThis(this), InParams.Editor, InParams.TreeViewRow); } if (InColumnName == FCommonOutlinerNames::Edit) { return SNew(SKeyAreaEditorSwitcher, SharedThis(this), Editor) .Visibility(this, &FChannelGroupOutlinerModel::GetKeyEditorVisibility); } if (InColumnName == FCommonOutlinerNames::KeyFrame) { EKeyNavigationButtons Buttons = EKeyNavigationButtons::AddKey; return SNew(SSequencerKeyNavigationButtons, SharedThis(this), Editor->GetSequencer()) .Buttons(Buttons); } if (InColumnName == FCommonOutlinerNames::Nav) { EKeyNavigationButtons Buttons = InParams.TreeViewRow->IsColumnVisible(FCommonOutlinerNames::KeyFrame) ? EKeyNavigationButtons::NavOnly : EKeyNavigationButtons::All; return SNew(SSequencerKeyNavigationButtons, SharedThis(this), Editor->GetSequencer()) .Buttons(Buttons); } if (InColumnName == FCommonOutlinerNames::ColorPicker) { return SNew(SOutlinerTrackColorPicker, SharedThis(this), InParams.Editor); } return nullptr; } EVisibility FChannelGroupOutlinerModel::GetKeyEditorVisibility() const { return GetChannels().Num() == 0 ? EVisibility::Collapsed : EVisibility::Visible; } FText FChannelGroupOutlinerModel::GetLabel() const { return GetDisplayText(); } FSlateFontInfo FChannelGroupOutlinerModel::GetLabelFont() const { return IsAnimated() ? FAppStyle::GetFontStyle("Sequencer.AnimationOutliner.ItalicFont") : FOutlinerItemModelMixin::GetLabelFont(); } FText FChannelGroupOutlinerModel::GetLabelToolTipText() const { return GetTooltipText(); } bool FChannelGroupOutlinerModel::HasCurves() const { return FChannelGroupModel::HasCurves(); } TOptional FChannelGroupOutlinerModel::GetUniquePathName() const { return FChannelGroupModel::GetUniquePathName(); } bool FChannelGroupOutlinerModel::CanDelete(FText* OutErrorMessage) const { return true; } void FChannelGroupOutlinerModel::Delete() { TArray PathFromTrack; TViewModelPtr Track = GetParentTrackNodeAndNamePath(SharedThis(this), PathFromTrack); Track->GetTrack()->Modify(); for (const FViewModelPtr& Channel : GetTrackAreaModelList()) { if (TViewModelPtr Section = Channel->FindAncestorOfType()) { Section->GetSectionInterface()->RequestDeleteKeyArea(PathFromTrack); } } } void FChannelGroupOutlinerModel::CreateCurveModels(TArray>& OutCurveModels) { FChannelGroupModel::CreateCurveModels(OutCurveModels); } void FChannelGroupOutlinerModel::BuildContextMenu(FMenuBuilder& MenuBuilder) { if (TViewModelPtr CommonChannel = WeakCommonChannelModel.Pin()) { CommonChannel->BuildContextMenu(MenuBuilder, TViewModelPtr(this)); } FOutlinerItemModelMixin::BuildContextMenu(MenuBuilder); BuildChannelOverrideMenu(MenuBuilder); } } // namespace UE::Sequencer #undef LOCTEXT_NAMESPACE