// Copyright Epic Games, Inc. All Rights Reserved. #include "MVVM/ViewModels/FolderModel.h" #include "MVVM/ViewModels/SequenceModel.h" #include "MVVM/ViewModels/TrackModel.h" #include "MVVM/ViewModels/LayerBarModel.h" #include "MVVM/ViewModels/ObjectBindingModel.h" #include "MVVM/ViewModels/ViewModelIterators.h" #include "MVVM/Extensions/IRecyclableExtension.h" #include "MVVM/Views/SOutlinerItemViewBase.h" #include "MVVM/ViewModels/SequencerEditorViewModel.h" #include "MVVM/ViewModels/OutlinerViewModelDragDropOp.h" #include "MVVM/TrackModelStorageExtension.h" #include "MVVM/ObjectBindingModelStorageExtension.h" #include "MVVM/FolderModelStorageExtension.h" #include "MVVM/SharedViewModelData.h" #include "MVVM/ViewModels/OutlinerColumns/OutlinerColumnTypes.h" #include "Sequencer.h" #include "SequencerNodeTree.h" #include "SequencerSettings.h" #include "SequencerUtilities.h" #include "Styling/AppStyle.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Widgets/Colors/SColorPicker.h" #include "SequencerOutlinerItemDragDropOp.h" #include "MovieScene.h" #include "MovieSceneBinding.h" #include "MovieSceneFolder.h" #include "Decorations/MovieSceneMuteSoloDecoration.h" #include "DragAndDrop/ActorDragDropOp.h" #include "ScopedTransaction.h" #include "Internationalization/Internationalization.h" #define LOCTEXT_NAMESPACE "SequencerFolderModel" namespace UE::Sequencer { TMap, FColor> FFolderModel::InitialFolderColors; bool FFolderModel::bFolderPickerWasCancelled = false; FFolderModel::FFolderModel(UMovieSceneFolder* Folder) : TrackAreaList(EViewModelListType::TrackArea) , WeakFolder(Folder) , OwnerModel(nullptr) { RegisterChildList(&TrackAreaList); } FFolderModel::~FFolderModel() { } UMovieSceneFolder* FFolderModel::GetFolder() const { return WeakFolder.Get(); } void FFolderModel::OnConstruct() { UMovieSceneFolder* Folder = WeakFolder.Get(); OwnerModel = FindAncestorOfType().Get(); check(Folder && OwnerModel); SetIdentifier(Folder->GetFolderName()); if (!IsLinked()) { Folder->EventHandlers.Link(this); } RepopulateChildren(); } void FFolderModel::RepopulateChildren() { UMovieSceneFolder* Folder = WeakFolder.Get(); if (!Folder) { DiscardAllChildren(); return; } FTrackModelStorageExtension* TrackStorage = OwnerModel->CastDynamic(); FObjectBindingModelStorageExtension* ObjectBindingStorage = OwnerModel->CastDynamic(); FFolderModelStorageExtension* FolderStorage = OwnerModel->CastDynamic(); TSharedPtr This = SharedThis(this); FViewModelChildren TrackAreaChildren = GetChildrenForList(&TrackAreaList); FViewModelChildren OutlinerChildren = GetChildrenForList(&OutlinerChildList); // Only need to every create a layer bar once if (!LayerBar) { TSharedPtr EditorViewModel = GetEditor(); TSharedPtr Sequencer = EditorViewModel->GetSequencerImpl(); if (Sequencer->GetSequencerSettings()->GetShowLayerBars()) { LayerBar = MakeShared(This); LayerBar->SetLinkedOutlinerItem(SharedThis(this)); TrackAreaChildren.AddChild(LayerBar); } } // Keep our existing children alive FScopedViewModelListHead RecycledOutliner(This, EViewModelListType::Recycled); OutlinerChildren.MoveChildrenTo(RecycledOutliner.GetChildren(), IRecyclableExtension::CallOnRecycle); // Create/recycle models for any children for (UMovieSceneFolder* ChildFolder : Folder->GetChildFolders()) { TSharedPtr ChildModel = FolderStorage->CreateModelForFolder(ChildFolder, This); OutlinerChildren.AddChild(ChildModel); } // Create/recycle models for any object bindings UMovieScene* MovieScene = OwnerModel->GetMovieScene(); if (MovieScene) { for (const FGuid& ObjectBinding : Folder->GetChildObjectBindings()) { const FMovieSceneBinding* Binding = MovieScene->FindBinding(ObjectBinding); if (ensureMsgf(Binding, TEXT("Folder '%s' contains an invalid binding reference ID: %s"), *Folder->GetFolderName().ToString(), *LexToString(ObjectBinding))) { TSharedPtr ObjectModel = ObjectBindingStorage->GetOrCreateModelForBinding(*Binding); OutlinerChildren.AddChild(ObjectModel); } } } // Create/recycle models for any tracks for (UMovieSceneTrack* Track : Folder->GetChildTracks()) { if (Track) { TSharedPtr TrackModel = TrackStorage->CreateModelForTrack(Track, This); OutlinerChildren.AddChild(TrackModel); } } } void FFolderModel::OnPostUndo() { RepopulateChildren(); } void FFolderModel::OnTrackAdded(UMovieSceneTrack* Track) { FTrackModelStorageExtension* TrackStorage = OwnerModel->CastDynamic(); TSharedPtr TrackModel = TrackStorage->CreateModelForTrack(Track, AsShared()); GetChildrenForList(&OutlinerChildList).AddChild(TrackModel); } void FFolderModel::OnTrackRemoved(UMovieSceneTrack* Track) { TSharedPtr TrackModel = GetChildrenOfType().FindBy(Track, &FTrackModel::GetTrack); if (TrackModel) { // When tracks are removed we put them back to the root. // If the operation moves the track into another folder, that model // will receieve the OnTrackAdded callback and reparent it again OwnerModel->GetChildList(EViewModelListType::Outliner) .AddChild(TrackModel); } } void FFolderModel::OnObjectBindingAdded(const FGuid& ObjectBinding) { TSharedPtr This = SharedThis(this); UMovieScene* MovieScene = OwnerModel->GetMovieScene(); const FMovieSceneBinding* Binding = MovieScene ? MovieScene->FindBinding(ObjectBinding) : nullptr; if (ensure(Binding)) { FObjectBindingModelStorageExtension* ObjectBindingStorage = OwnerModel->CastDynamic(); TSharedPtr ObjectModel = ObjectBindingStorage->GetOrCreateModelForBinding(*Binding); GetChildList(EViewModelListType::Outliner) .AddChild(ObjectModel); } } void FFolderModel::OnObjectBindingRemoved(const FGuid& ObjectBinding) { TSharedPtr ObjectModel = GetChildrenOfType().FindBy(ObjectBinding, &FObjectBindingModel::GetObjectGuid); if (ObjectModel) { // When tracks are removed we put them back to the root. // If the operation moves the track into another folder, that model // will receieve the OnTrackAdded callback and reparent it again OwnerModel->GetChildList(EViewModelListType::Outliner) .AddChild(ObjectModel); } } void FFolderModel::OnChildFolderAdded(UMovieSceneFolder* Folder) { TSharedPtr This = SharedThis(this); FFolderModelStorageExtension* FolderStorage = OwnerModel->CastDynamic(); TSharedPtr ChildModel = FolderStorage->CreateModelForFolder(Folder, This); GetChildList(EViewModelListType::Outliner) .AddChild(ChildModel); } void FFolderModel::OnChildFolderRemoved(UMovieSceneFolder* Folder) { TSharedPtr FolderModel = GetChildrenOfType().FindBy(Folder, &FFolderModel::GetFolder); if (FolderModel) { // When folders are removed we remove them entirely - // If they get added to the root set, FFolderModelStorageExtension will pick up that operation FolderModel->RemoveFromParent(); } } FOutlinerSizing FFolderModel::GetOutlinerSizing() const { const float CompactHeight = 28.f; FViewDensityInfo Density = GetEditor()->GetViewDensity(); return FOutlinerSizing(Density.UniformHeight.Get(CompactHeight)); } void FFolderModel::GetIdentifierForGrouping(TStringBuilder<128>& OutString) const { FOutlinerItemModel::GetIdentifier().ToString(OutString); } FTrackAreaParameters FFolderModel::GetTrackAreaParameters() const { FTrackAreaParameters Params; Params.LaneType = ETrackAreaLaneType::Inline; return Params; } FViewModelVariantIterator FFolderModel::GetTrackAreaModelList() const { return &TrackAreaList; } bool FFolderModel::CanRename() const { return true; } void FFolderModel::Rename(const FText& NewName) { UMovieSceneFolder* FolderObject = WeakFolder.Get(); if (!FolderObject) { return; } FName DesiredName(*NewName.ToString()); if (FolderObject->GetFolderName() != FName(*NewName.ToString())) { TArray SiblingNames; TSharedPtr Parent = GetParent(); if (ensure(Parent)) { for (TSharedPtr Sibling : Parent->GetChildrenOfType()) { UMovieSceneFolder* SiblingFolder = Sibling->GetFolder(); if (SiblingFolder != FolderObject) { SiblingNames.Add(SiblingFolder->GetFolderName()); } } } FName UniqueName = FSequencerUtilities::GetUniqueName(DesiredName, SiblingNames); const FScopedTransaction Transaction( NSLOCTEXT( "SequencerFolderNode", "RenameFolder", "Rename folder." ) ); FolderObject->SetFolderName( UniqueName ); SetIdentifier(UniqueName); } } bool FFolderModel::CanDrag() const { return true; } void FFolderModel::BuildContextMenu(FMenuBuilder& MenuBuilder) { const TSharedPtr Sequencer = StaticCastSharedPtr(GetEditor()->GetSequencer()); const bool bIsReadOnly = Sequencer->GetRootMovieSceneSequence()->GetMovieScene()->IsReadOnly(); MenuBuilder.BeginSection("ObjectBindings"); // Create the section to allow extension if (!bIsReadOnly) { Sequencer->BuildAddObjectBindingsMenu(MenuBuilder); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("AddTracks"); // Create the section to allow extension if (!bIsReadOnly) { Sequencer->BuildAddTrackMenu(MenuBuilder); } MenuBuilder.EndSection(); FOutlinerItemModel::BuildContextMenu(MenuBuilder); MenuBuilder.BeginSection("Folder", LOCTEXT("FolderContextMenuSectionName", "Folder")); { MenuBuilder.AddMenuEntry( LOCTEXT("SetColor", "Set Color"), LOCTEXT("SetColorTooltip", "Set the color for the selected folders"), FSlateIcon(), FUIAction( FExecuteAction::CreateSP(this, &FFolderModel::SetFolderColor)) ); } MenuBuilder.EndSection(); } void FFolderModel::BuildSidebarMenu(FMenuBuilder& MenuBuilder) { FOutlinerItemModel::BuildSidebarMenu(MenuBuilder); MenuBuilder.BeginSection(TEXT("Folder"), LOCTEXT("FolderContextMenuSectionName", "Folder")); { MenuBuilder.AddMenuEntry( LOCTEXT("SetColor", "Set Color"), LOCTEXT("SetColorTooltip", "Set the color for the selected folders"), FSlateIcon(), FUIAction(FExecuteAction::CreateSP(this, &FFolderModel::SetFolderColor)) ); } MenuBuilder.EndSection(); } TSharedPtr FFolderModel::CreateOutlinerViewForColumn(const FCreateOutlinerViewParams& InParams, const FName& InColumnName) { class SOutlinerFolderView : public SOutlinerItemViewBase { public: void Construct(const FArguments& InArgs, TWeakPtr InWeakModel, TWeakPtr InWeakEditor, const TSharedRef& InTableRow) { IsExpandedAttribute = MakeAttributeSP(&InTableRow.Get(), &ISequencerTreeViewRow::IsItemExpanded); SOutlinerItemViewBase::Construct(InArgs, TWeakViewModelPtr(InWeakModel), InWeakEditor, InTableRow); } const FSlateBrush* GetIconBrush() const { return IsExpandedAttribute.Get() ? FAppStyle::GetBrush( "ContentBrowser.AssetTreeFolderOpen" ) : FAppStyle::GetBrush( "ContentBrowser.AssetTreeFolderClosed" ); } TAttribute IsExpandedAttribute; }; if (InColumnName == FCommonOutlinerNames::Label) { return SNew(SOutlinerFolderView, SharedThis(this), InParams.Editor, InParams.TreeViewRow); } return nullptr; } void FFolderModel::SetFolderColor() { bFolderPickerWasCancelled = false; InitialFolderColors.Empty(); TSharedPtr EditorViewModel = GetEditor(); TSharedPtr Sequencer = EditorViewModel->GetSequencerImpl(); TArray MovieSceneFolders; Sequencer->GetSelectedFolders(MovieSceneFolders); if (MovieSceneFolders.Num() == 0) { return; } for (UMovieSceneFolder* MovieSceneFolder : MovieSceneFolders) { InitialFolderColors.Add(MovieSceneFolder, MovieSceneFolder->GetFolderColor()); } FColorPickerArgs PickerArgs; PickerArgs.bUseAlpha = false; PickerArgs.DisplayGamma = TAttribute::Create( TAttribute::FGetter::CreateUObject(GEngine, &UEngine::GetDisplayGamma) ); PickerArgs.InitialColor = (*InitialFolderColors.CreateIterator()).Value.ReinterpretAsLinear(); PickerArgs.OnColorCommitted = FOnLinearColorValueChanged::CreateSP(this, &FFolderModel::OnColorPickerPicked); PickerArgs.OnColorPickerWindowClosed = FOnWindowClosed::CreateSP(this, &FFolderModel::OnColorPickerClosed); PickerArgs.OnColorPickerCancelled = FOnColorPickerCancelled::CreateSP(this, &FFolderModel::OnColorPickerCancelled ); OpenColorPicker(PickerArgs); } void FFolderModel::OnColorPickerPicked(FLinearColor NewFolderColor) { TSharedPtr EditorViewModel = GetEditor(); TSharedPtr Sequencer = EditorViewModel->GetSequencerImpl(); TArray MovieSceneFolders; Sequencer->GetSelectedFolders(MovieSceneFolders); for (UMovieSceneFolder* MovieSceneFolder : MovieSceneFolders) { MovieSceneFolder->SetFolderColor(NewFolderColor.ToFColor(false)); } } void FFolderModel::OnColorPickerClosed(const TSharedRef& Window) { if (!bFolderPickerWasCancelled) { TSharedPtr EditorViewModel = GetEditor(); TSharedPtr Sequencer = EditorViewModel->GetSequencerImpl(); TArray MovieSceneFolders; Sequencer->GetSelectedFolders(MovieSceneFolders); const FScopedTransaction Transaction(NSLOCTEXT("SequencerFolderNode", "SetFolderColor", "Set Folder Color")); for (UMovieSceneFolder* MovieSceneFolder : MovieSceneFolders) { FColor CurrentColor = MovieSceneFolder->GetFolderColor(); FColor InitialFolderColor = InitialFolderColors.Contains(MovieSceneFolder) ? InitialFolderColors[MovieSceneFolder] : FColor(); MovieSceneFolder->SetFolderColor(InitialFolderColor); MovieSceneFolder->Modify(); MovieSceneFolder->SetFolderColor(CurrentColor); } } InitialFolderColors.Empty(); } void FFolderModel::OnColorPickerCancelled(FLinearColor NewFolderColor) { bFolderPickerWasCancelled = true; TSharedPtr EditorViewModel = GetEditor(); TSharedPtr Sequencer = EditorViewModel->GetSequencerImpl(); TArray MovieSceneFolders; Sequencer->GetSelectedFolders(MovieSceneFolders); for (UMovieSceneFolder* MovieSceneFolder : MovieSceneFolders) { if (InitialFolderColors.Contains(MovieSceneFolder)) { MovieSceneFolder->SetFolderColor(InitialFolderColors[MovieSceneFolder]); } } InitialFolderColors.Empty(); } FText FFolderModel::GetLabel() const { UMovieSceneFolder* Folder = GetFolder(); return Folder ? FText::FromName(Folder->GetFolderName()) : LOCTEXT("ExpiredText", "<>"); } const FSlateBrush* FFolderModel::GetIconBrush() const { return FAppStyle::GetBrush( "ContentBrowser.AssetTreeFolderClosed" ); } FSlateColor FFolderModel::GetIconTint() const { UMovieSceneFolder* Folder = GetFolder(); if (!Folder) { return FLinearColor::White; } if (IsDimmed()) { return Folder->GetFolderColor().ReinterpretAsLinear().Desaturate(0.6f); } return Folder->GetFolderColor(); } FSlateColor FFolderModel::GetLabelColor() const { if (IsDimmed()) { return FSlateColor::UseSubduedForeground(); } return FSlateColor::UseForeground(); } void FFolderModel::SortChildren() { ISortableExtension::SortChildren(SharedThis(this), ESortingMode::Default); } FSortingKey FFolderModel::GetSortingKey() const { FSortingKey SortingKey; if (UMovieSceneFolder* Folder = GetFolder()) { SortingKey.DisplayName = FText::FromName(Folder->GetFolderName()); SortingKey.CustomOrder = Folder->GetSortingOrder(); } return SortingKey.PrioritizeBy(3); } void FFolderModel::SetCustomOrder(int32 InCustomOrder) { if (UMovieSceneFolder* Folder = GetFolder()) { Folder->SetSortingOrder(InCustomOrder); } } TOptional FFolderModel::CanAcceptDrop(const FViewModelPtr& TargetModel, const FDragDropEvent& DragDropEvent, EItemDropZone ItemDropZone) { TSharedPtr ActorDragDropOp = DragDropEvent.GetOperationAs(); if (ActorDragDropOp) { return ItemDropZone; } TSharedPtr DragDropOp = DragDropEvent.GetOperationAs(); if (!DragDropOp) { return TOptional(); } DragDropOp->ResetToDefaultToolTip(); // Prevent taking any parent that's part of the dragged node hierarchy from being put inside a child of itself // This is done first before the other checks so that the UI stays consistent as you move between them, otherwise // when you are above/below a node it reports this error, but if you were on top of a node it would do the standard // no-drag-drop due to OntoItem being blocked. if (!DragDropOp->ValidateParentChildDrop(*this)) { return TOptional(); } for (const TWeakViewModelPtr& WeakModel : DragDropOp->GetDraggedViewModels()) { FViewModelPtr Model = WeakModel.Pin(); if (!Model) { // Silently allow null models continue; } if (Model.Get() == this || Model == TargetModel) { // Cannot drop onto self return TOptional(); } if (Model->IsA()) { // We can always drop folders into other folders } else if (TViewModelPtr Object = Model.ImplicitCast()) { if (TViewModelPtr ParentObject = Model->CastParent()) { // This operation should not have been allowed in the first place (child objects should never be draggable) return TOptional(); } } else if (TViewModelPtr Track = Model.ImplicitCast()) { if (TViewModelPtr ParentObject = Model->CastParent()) { // This operation should not have been allowed in the first place (tracks bound to an object should never be draggable) return TOptional(); } } else { // Unknown model type - do we silent not drag these or return an error? // For now we disallow the drag, but maybe that's not the right choice return TOptional(); } } // The dragged nodes were either all in folders, or all at the sequencer root. return ItemDropZone; } void FFolderModel::PerformDropActors(const FViewModelPtr& TargetModel, TSharedPtr ActorDragDropEvent, TSharedPtr AttachAfter) { UMovieSceneFolder* Folder = WeakFolder.Get(); if (!Folder) { return; } TSharedPtr Sequencer = StaticCastSharedPtr(GetEditor()->GetSequencer()); if (!Sequencer) { return; } TArray PossessableGuids = FSequencerUtilities::AddActors(Sequencer.ToSharedRef(), ActorDragDropEvent->Actors); for (FGuid PossessableGuid : PossessableGuids) { Folder->AddChildObjectBinding(PossessableGuid); } } void FFolderModel::PerformDropOutliner(const FViewModelPtr& TargetModel, TSharedPtr OutlinerDragDropEvent, TSharedPtr AttachAfter) { UMovieSceneFolder* Folder = WeakFolder.Get(); if (!Folder) { return; } // Drop handing for outliner drag/drop operations. // Warning: this handler may be dragging nodes from a *different* sequence FViewModelChildren OutlinerChildren = GetChildList(EViewModelListType::Outliner); UMovieScene* MovieScene = Folder->GetTypedOuter(); if (MovieScene) { MovieScene->SetFlags(RF_Transactional); MovieScene->Modify(); } for (const TWeakViewModelPtr& WeakModel : OutlinerDragDropEvent->GetDraggedViewModels()) { FViewModelPtr DraggedModel = WeakModel.Pin(); if (!DraggedModel || DraggedModel == AttachAfter) { continue; } TViewModelPtr ExistingParentFolder = DraggedModel->CastParent(); UMovieSceneFolder* OldFolder = ExistingParentFolder ? ExistingParentFolder->GetFolder() : nullptr; bool bSuccess = false; // Handle dropoping a folder into another folder // @todo: if we ever support folders within object bindings this will need to have better validation if (TSharedPtr DraggedFolderModel = DraggedModel.ImplicitCast()) { if (UMovieSceneFolder* DraggedFolder = DraggedFolderModel->GetFolder()) { if (OldFolder) { OldFolder->SetFlags(RF_Transactional); OldFolder->Modify(); OldFolder->RemoveChildFolder(DraggedFolder); } else if (MovieScene) { MovieScene->RemoveRootFolder(DraggedFolder); } // Give this folder a unique name inside its new parent if necessary FName FolderName = Folder->MakeUniqueChildFolderName(DraggedFolder->GetFolderName()); if (FolderName != DraggedFolder->GetFolderName()) { DraggedFolder->SetFlags(RF_Transactional); DraggedFolder->Modify(); DraggedFolder->SetFolderName(FolderName); } Folder->AddChildFolder(DraggedFolder); bSuccess = true; } } else if (TSharedPtr ObjectBinding = DraggedModel.ImplicitCast()) { TViewModelPtr ParentObject = DraggedModel->CastParent(); // Don't allow dropping an object binding if it has an object parent if (ParentObject == nullptr) { if (OldFolder) { OldFolder->SetFlags(RF_Transactional); OldFolder->Modify(); OldFolder->RemoveChildObjectBinding(ObjectBinding->GetObjectGuid()); } Folder->AddChildObjectBinding(ObjectBinding->GetObjectGuid()); bSuccess = true; } } else if (TSharedPtr TrackModel = DraggedModel.ImplicitCast()) { UMovieSceneTrack* Track = TrackModel->GetTrack(); TViewModelPtr ParentObject = DraggedModel->CastParent(); // Don't allow dropping a track if it has an object parent if (ParentObject == nullptr && Track != nullptr) { if (OldFolder) { OldFolder->SetFlags(RF_Transactional); OldFolder->Modify(); OldFolder->RemoveChildTrack(Track); } Folder->AddChildTrack(Track); bSuccess = true; } } if (bSuccess) { // Attach it to the right node, possibly setting its parent too OutlinerChildren.InsertChild(DraggedModel, AttachAfter); } } } void FFolderModel::PerformDrop(const FViewModelPtr& TargetModel, const FDragDropEvent& DragDropEvent, EItemDropZone InItemDropZone) { FViewModelHierarchyOperation ScopedOperation(GetSharedData()); UMovieSceneFolder* Folder = WeakFolder.Get(); if (!Folder) { return; } const FScopedTransaction Transaction(FText::Format(LOCTEXT("MoveItems", "Move into {0}"), FText::FromName(Folder->GetFolderName()))); Folder->SetFlags(RF_Transactional); Folder->Modify(); TSharedPtr AttachAfter; if (TargetModel) { if (InItemDropZone == EItemDropZone::AboveItem) { AttachAfter = TargetModel->GetPreviousSibling(); } else if (InItemDropZone == EItemDropZone::BelowItem) { AttachAfter = TargetModel; } } // Open this folder if an item was dropped into the folder if (InItemDropZone == EItemDropZone::OntoItem && !TargetModel) { SetExpansion(true); } TSharedPtr ActorDragDropOp = DragDropEvent.GetOperationAs(); if (ActorDragDropOp) { PerformDropActors(TargetModel, ActorDragDropOp, AttachAfter); } TSharedPtr OutlinerDragDropOp = DragDropEvent.GetOperationAs(); if (OutlinerDragDropOp) { PerformDropOutliner(TargetModel, OutlinerDragDropOp, AttachAfter); } // Forcibly update sorting int32 CustomSort = 0; for (const TViewModelPtr& Sortable : GetChildrenOfType()) { Sortable->SetCustomOrder(CustomSort); ++CustomSort; } } bool FFolderModel::CanDelete(FText* OutErrorMessage) const { for (const TViewModelPtr& Child : GetChildrenOfType(EViewModelListType::Outliner)) { if (!Child->CanDelete(OutErrorMessage)) { return false; } } return true; } void FFolderModel::Delete() { check(OwnerModel); // When we delete a folder, we also delete all the items inside it. Some of // these items (like object bindings) remove themselves from any parent folder, // which is us in this case, and then remove themselves entirely from the // movie scene. Since these operations call events (such as IFolderEventHandler // events, which we implement), it messes up the list of children that we want // to iterate over here. We therefore have to cache that list before using it. TArray> DeletableChildren( GetChildrenOfType(EViewModelListType::Outliner).ToArray()); for (const TViewModelPtr& Child : DeletableChildren) { Child->Delete(); } UMovieScene* MovieScene = OwnerModel->GetMovieScene(); // Remove from a parent folder if necessary. if (TViewModelPtr ParentFolder = CastParent()) { ParentFolder->GetFolder()->Modify(); ParentFolder->GetFolder()->RemoveChildFolder(GetFolder()); } else { MovieScene->Modify(); MovieScene->RemoveRootFolder(GetFolder()); } } bool FFolderModel::IsMuted() const { UMovieSceneFolder* Folder = WeakFolder.Get(); if (!Folder) { return false; } if (UMovieSceneMuteSoloDecoration* MuteSoloDecoration = Folder->FindDecoration()) { return MuteSoloDecoration->IsMuted(); } return false; } void FFolderModel::SetIsMuted(bool bIsMuted) { UMovieSceneFolder* Folder = WeakFolder.Get(); if (!Folder) { return; } const bool bAlwaysMarkDirty = false; Folder->Modify(bAlwaysMarkDirty); if (UMovieSceneMuteSoloDecoration* MuteSoloDecoration = Folder->GetOrCreateDecoration()) { MuteSoloDecoration->SetMuted(bIsMuted); } } bool FFolderModel::IsSolo() const { UMovieSceneFolder* Folder = WeakFolder.Get(); if (!Folder) { return false; } if (UMovieSceneMuteSoloDecoration* MuteSoloDecoration = Folder->FindDecoration()) { return MuteSoloDecoration->IsSoloed(); } return false; } void FFolderModel::SetIsSoloed(bool bIsSoloed) { UMovieSceneFolder* Folder = WeakFolder.Get(); if (!Folder) { return; } const bool bAlwaysMarkDirty = false; Folder->Modify(bAlwaysMarkDirty); if (UMovieSceneMuteSoloDecoration* MuteSoloDecoration = Folder->GetOrCreateDecoration()) { MuteSoloDecoration->SetSoloed(bIsSoloed); } } } // namespace UE::Sequencer #undef LOCTEXT_NAMESPACE