// Copyright Epic Games, Inc. All Rights Reserved. #include "Dialogs/SDeleteAssetsDialog.h" #include "AssetRegistry/AssetData.h" #include "AssetRegistry/AssetRegistryModule.h" #include "AssetRegistry/IAssetRegistry.h" #include "Framework/Commands/UIAction.h" #include "Framework/Commands/UICommandList.h" #include "Widgets/Notifications/SProgressBar.h" #include "Widgets/Text/STextBlock.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SComboButton.h" #include "Widgets/Views/SListView.h" #include "Widgets/Input/SCheckBox.h" #include "Styling/AppStyle.h" #include "Settings/EditorLoadingSavingSettings.h" #include "EditorDirectories.h" #include "FileHelpers.h" #include "Misc/MessageDialog.h" #include "IContentBrowserSingleton.h" #include "ContentBrowserModule.h" #include "Editor.h" #include "Editor/Transactor.h" #include "Framework/Commands/GenericCommands.h" #include "Subsystems/AssetEditorSubsystem.h" #define LOCTEXT_NAMESPACE "SDeleteAssetsDialog" namespace DeleteAssetsView { /** IDs for list columns */ static const FName ColumnID_Asset( "Asset" ); static const FName ColumnID_AssetClass( "Class" ); static const FName ColumnID_DiskReferences( "DiskReferences" ); static const FName ColumnID_MemoryReferences( "MemoryReferences" ); } static FLinearColor DangerColor( 0.715465432, 0.034230207, 0 ); static FLinearColor WarningColor( 1, 1, 0 ); ////////////////////////////////////////////////////////////////////////// // SPendingDeleteRow class SPendingDeleteRow : public SMultiColumnTableRow< TSharedPtr > { public: SLATE_BEGIN_ARGS( SPendingDeleteRow ) { } SLATE_END_ARGS() void Construct( const FArguments& InArgs, const TSharedRef& InOwnerTableView, TSharedPtr InItem ) { Item = InItem; SMultiColumnTableRow< TSharedPtr >::Construct(FSuperRowType::FArguments(), InOwnerTableView); } virtual TSharedRef GenerateWidgetForColumn( const FName& ColumnName ) { if ( ColumnName == DeleteAssetsView::ColumnID_Asset ) { return SNew( SHorizontalBox ) + SHorizontalBox::Slot() .AutoWidth() .Padding( 3, 0, 0, 0 ) [ SNew( STextBlock ) .Text(FText::FromString(Item->GetObject()->GetName())) ]; } else if ( ColumnName == DeleteAssetsView::ColumnID_AssetClass ) { return SNew( STextBlock ) .Text(FText::FromString(Item->GetObject()->GetClass()->GetName())); } else if ( ColumnName == DeleteAssetsView::ColumnID_DiskReferences ) { FFormatNamedArguments Args; Args.Add( TEXT( "AssetCount" ), FText::AsNumber( Item->RemainingDiskReferences ) ); FText OnDiskCountText = Item->RemainingDiskReferences > 1 ? FText::Format( LOCTEXT( "OnDiskAssetReferences", "{AssetCount} References" ), Args ) : FText::Format( LOCTEXT( "OnDiskAssetReference", "{AssetCount} Reference" ), Args ); return SNew( STextBlock ) .Text( OnDiskCountText ) .Visibility( Item->RemainingDiskReferences > 0 ? EVisibility::Visible : EVisibility::Hidden ); } else if ( ColumnName == DeleteAssetsView::ColumnID_MemoryReferences ) { FFormatNamedArguments Args; Args.Add( TEXT( "ReferenceCount" ), FText::AsNumber( Item->RemainingMemoryReferences ) ); FText InMemoryCountText = Item->RemainingMemoryReferences > 1 ? FText::Format( LOCTEXT( "InMemoryReferences", "{ReferenceCount} References" ), Args ) : FText::Format( LOCTEXT( "OnDiskReference", "{ReferenceCount} Reference" ), Args ); return SNew( STextBlock ) .Text( InMemoryCountText ) .Visibility( Item->RemainingMemoryReferences > 0 ? EVisibility::Visible : EVisibility::Hidden ); } return SNullWidget::NullWidget; } private: TSharedPtr Item; }; ////////////////////////////////////////////////////////////////////////// // SDeleteAssetsDialog SDeleteAssetsDialog::~SDeleteAssetsDialog() { DeleteModel->OnStateChanged().RemoveAll( this ); } void SDeleteAssetsDialog::Construct( const FArguments& InArgs, TSharedRef InDeleteModel ) { //bIsActiveTimerRegistered = false; bIsActiveTimerRegistered = true; RegisterActiveTimer( 0.f, FWidgetActiveTimerDelegate::CreateSP( this, &SDeleteAssetsDialog::TickDeleteModel ) ); DeleteModel = InDeleteModel; // Save off the attributes ParentWindow = InArgs._ParentWindow; ReferencerCommands = TSharedPtr< FUICommandList >(new FUICommandList); ReferencerCommands->MapAction(FGenericCommands::Get().Delete, FUIAction( FExecuteAction::CreateSP( this, &SDeleteAssetsDialog::ExecuteDeleteReferencers ), FCanExecuteAction::CreateSP( this, &SDeleteAssetsDialog::CanExecuteDeleteReferencers ) ) ); // Create the widgets ChildSlot [ SAssignNew(RootContainer, SBorder) .BorderImage( FAppStyle::GetBrush( "AssetDeleteDialog.Background" ) ) .Padding(10.0f) ]; DeleteModel->OnStateChanged().AddRaw(this, &SDeleteAssetsDialog::HandleDeleteModelStateChanged); // Manually fire the state changed event so that we are setup for the initial state. HandleDeleteModelStateChanged(DeleteModel->GetState()); } TSharedRef SDeleteAssetsDialog::BuildProgressDialog() { return SNew( SVerticalBox ) // Show Progress Text + SVerticalBox::Slot() .VAlign(VAlign_Center) .FillHeight( 1.0f ) [ SNew( SVerticalBox ) + SVerticalBox::Slot() .Padding( 5.0f, 0 ) [ SNew( STextBlock ) .Text( this, &SDeleteAssetsDialog::ScanningText ) ] // Show Progress + SVerticalBox::Slot() .AutoHeight() .Padding( 5.0f, 10.0f ) [ SNew( SProgressBar ) .Percent( this, &SDeleteAssetsDialog::ScanningProgressFraction ) ] ]; } TSharedRef SDeleteAssetsDialog::BuildDeleteDialog() { const auto* LoadingSavingSettings = GetDefault(); FFormatNamedArguments Args; Args.Add( TEXT( "OnDiskReferences" ), FText::AsNumber( DeleteModel->GetAssetReferences().Num() ) ); TSharedRef< SHeaderRow > HeaderRowWidget = SNew( SHeaderRow ) + SHeaderRow::Column( DeleteAssetsView::ColumnID_Asset ) .DefaultLabel( LOCTEXT( "Column_AssetName", "Asset" ) ) .HAlignHeader( EHorizontalAlignment::HAlign_Left ) .FillWidth(0.5f) + SHeaderRow::Column( DeleteAssetsView::ColumnID_AssetClass ) .DefaultLabel( LOCTEXT( "Column_AssetClass", "Class" ) ) .HAlignHeader( EHorizontalAlignment::HAlign_Left ) .FillWidth( 0.25f ) + SHeaderRow::Column( DeleteAssetsView::ColumnID_DiskReferences ) .DefaultLabel( LOCTEXT( "Column_DiskReferences", "Asset Referencers" ) ) .HAlignHeader( EHorizontalAlignment::HAlign_Left ) .FillWidth( 0.25f ) + SHeaderRow::Column( DeleteAssetsView::ColumnID_MemoryReferences ) .DefaultLabel( LOCTEXT( "Column_MemoryReferences", "Memory References" ) ) .HAlignHeader( EHorizontalAlignment::HAlign_Left ) .FillWidth( 0.25f ); return SNew( SVerticalBox ) // The to be deleted assets + SVerticalBox::Slot() .FillHeight( 0.5f ) .Padding( 5.0f ) [ SNew( SBorder ) .BorderImage( FAppStyle::GetBrush( "ToolPanel.GroupBorder" ) ) .Padding( FMargin(0, 0, 0, 3) ) [ SNew( SVerticalBox ) // Attempting delete text + SVerticalBox::Slot() .AutoHeight() [ SNew( SBorder ) .BorderImage( FAppStyle::GetBrush( "DetailsView.CategoryTop" ) ) .BorderBackgroundColor( FLinearColor( .6, .6, .6, 1.0f ) ) .Padding(3.0f) [ SNew( STextBlock ) .Text( LOCTEXT( "AttemptingDelete", "Pending Deleted Assets" ) ) .Font( FAppStyle::GetFontStyle( "BoldFont" ) ) .ShadowOffset( FVector2D( 1.0f, 1.0f ) ) ] ] + SVerticalBox::Slot() .FillHeight(1.0f) [ SAssignNew( ObjectsToDeleteList, SListView< TSharedPtr > ) .ListItemsSource( DeleteModel->GetPendingDeletedAssets() ) .OnGenerateRow( this, &SDeleteAssetsDialog::HandleGenerateAssetRow ) .HeaderRow( HeaderRowWidget ) ] ] ] + SVerticalBox::Slot() .AutoHeight() .Padding( 5.0f ) [ SNew( SBorder ) .BorderBackgroundColor( FLinearColor::Red ) .BorderImage( FAppStyle::GetBrush( "ToolPanel.GroupBorder" ) ) .Visibility( this, &SDeleteAssetsDialog::GetReferencesVisiblity ) .Padding(5.0f) [ SNew( STextBlock ) .Text( LOCTEXT( "References", "Some of the assets being deleted are still referenced in memory." ) ) ] ] + SVerticalBox::Slot() .AutoHeight() .Padding( 5.0f ) [ SNew( SBorder ) .BorderBackgroundColor( FLinearColor::Yellow ) .BorderImage( FAppStyle::GetBrush( "ToolPanel.GroupBorder" ) ) .Visibility( this, &SDeleteAssetsDialog::GetUndoVisiblity ) .Padding( 5.0f ) [ SNew( STextBlock ) .Text( LOCTEXT( "DeleteUndo", "There are references in the undo history, so the undo history will be cleared." ) ) ] ] + SVerticalBox::Slot() .FillHeight( 1.0f ) .Padding( 5.0f ) [ SNew( SBorder ) .BorderImage( FAppStyle::GetBrush( "ToolPanel.GroupBorder" ) ) .Padding( FMargin( 0, 0, 0, 3 ) ) .Visibility( this, &SDeleteAssetsDialog::GetAssetReferencesVisiblity) [ SNew( SVerticalBox ) // Pending Deletes + SVerticalBox::Slot() .AutoHeight() [ SNew( SBorder ) .BorderImage( FAppStyle::GetBrush( "DetailsView.CategoryTop" ) ) .BorderBackgroundColor( FLinearColor( .6, .6, .6, 1.0f ) ) .Padding( 3.0f ) [ SNew( STextBlock ) .Text( LOCTEXT( "AssetsReferencingPendingDeletedAssets", "Assets Referencing the Pending Deleted Assets" ) ) .Font( FAppStyle::GetFontStyle( "BoldFont" ) ) .ShadowOffset( FVector2D( 1.0f, 1.0f ) ) ] ] // The Assets Still Using The To-Be-Deleted Assets + SVerticalBox::Slot() .FillHeight( 1.0f ) [ SDeleteAssetsDialog::MakeAssetViewForReferencerAssets() ] ] ] + SVerticalBox::Slot() .AutoHeight() .Padding( 5.0f ) [ SNew( SBorder ) .BorderImage( FAppStyle::GetBrush( "ToolPanel.GroupBorder" ) ) .Padding( 0.0f ) [ SNew( SVerticalBox ) // How do you want to handle this? + SVerticalBox::Slot() .AutoHeight() [ SNew( SBorder ) .BorderImage( FAppStyle::GetBrush( "DetailsView.CategoryTop" ) ) .BorderBackgroundColor( FLinearColor( .6, .6, .6, 1.0f ) ) .Padding( 3.0f ) [ SNew( SHorizontalBox ) + SHorizontalBox::Slot() .FillWidth( 1.0f ) .HAlign( HAlign_Center ) [ SNew( STextBlock ) .Text( this, &SDeleteAssetsDialog::GetHandleText ) .Font( FAppStyle::GetFontStyle( "BoldFont" ) ) .ShadowOffset( FVector2D( 1.0f, 1.0f ) ) ] ] ] + SVerticalBox::Slot() .AutoHeight() .Padding(6,4) [ SAssignNew(DeleteSourceFilesCheckbox, SCheckBox) .Visibility(this, &SDeleteAssetsDialog::GetDeleteSourceFilesVisibility) .IsChecked(LoadingSavingSettings->bDeleteSourceFilesWithAssets ? ECheckBoxState::Checked : ECheckBoxState::Unchecked) [ SNew(STextBlock) .Text(LOCTEXT("DeleteSourceFiles", "Also delete related source content files")) .ToolTip( SNew(SToolTip) .Text(this, &SDeleteAssetsDialog::GetDeleteSourceContentTooltip) ) ] ] + SVerticalBox::Slot() .AutoHeight() .Padding(0,4) [ SNew( SHorizontalBox ) + SHorizontalBox::Slot() .FillWidth( 1.0f ) .Padding( 6, 0 ) [ SNew( SBorder ) .BorderImage( FAppStyle::GetBrush( "NoBorder" ) ) .Visibility( this, &SDeleteAssetsDialog::GetReplaceReferencesVisibility ) [ ( DeleteModel->CanReplaceReferences() ? BuildReplaceReferencesWidget() : BuildCantUseReplaceReferencesWidget() ) ] ] + SHorizontalBox::Slot() .FillWidth( 1.0f ) .Padding( 6, 0 ) [ SNew( SBorder ) .BorderImage( FAppStyle::GetBrush( "NoBorder" ) ) .Visibility( this, &SDeleteAssetsDialog::GetForceDeleteVisibility ) [ BuildForceDeleteWidget() ] ] + SHorizontalBox::Slot() .FillWidth( 1.0f ) .Padding( 6, 0 ) [ SNew( SBorder ) .BorderImage( FAppStyle::GetBrush( "NoBorder" ) ) .Visibility( this, &SDeleteAssetsDialog::GetDeleteVisibility ) [ SNew( SButton ) .HAlign( HAlign_Center ) .Text( LOCTEXT( "Delete", "Delete" ) ) .ToolTipText( LOCTEXT( "DeleteTooltipText", "Perform the delete" ) ) .ButtonStyle(FAppStyle::Get(), "FlatButton.Danger") .TextStyle(FAppStyle::Get(), "FlatButton.DefaultTextStyle") .OnClicked(this, &SDeleteAssetsDialog::Delete) ] ] + SHorizontalBox::Slot() .FillWidth( 1.0f ) .Padding( 6, 0 ) [ SNew( SBorder ) .BorderImage( FAppStyle::GetBrush( "NoBorder" ) ) .VAlign( EVerticalAlignment::VAlign_Bottom ) [ SNew( SButton ) .HAlign( HAlign_Center ) .Text( LOCTEXT( "Cancel", "Cancel" ) ) .ToolTipText( LOCTEXT( "CancelDeleteTooltipText", "Cancel the delete" ) ) .ButtonStyle(FAppStyle::Get(), "FlatButton.Default") .TextStyle(FAppStyle::Get(), "FlatButton.DefaultTextStyle") .OnClicked( this, &SDeleteAssetsDialog::Cancel ) ] ] ] ] ]; } FText SDeleteAssetsDialog::GetHandleText() const { if ( CanDelete() ) { return LOCTEXT( "AreYouSure", "Are you sure you want to delete these assets?" ); } else { return LOCTEXT( "HandleIt", "How do you want to handle this?" ); } } FText SDeleteAssetsDialog::GetDeleteSourceContentTooltip() const { FString AllFiles; static const int32 MaxNumPathsToShow = 25; const auto& AllFileCounts = DeleteModel->GetPendingDeletedSourceFileCounts(); int32 TotalCount = 0, NumPrinted = 0; for (const auto& PathAndAssetCount : AllFileCounts) { // If this path is no longer referenced by deleted files, it's toast. if (PathAndAssetCount.Value == 0) { ++TotalCount; if (TotalCount <= MaxNumPathsToShow) { if (NumPrinted != 0) { AllFiles += TEXT("\n"); } AllFiles += PathAndAssetCount.Key; ++NumPrinted; } } } FText RootText; FFormatOrderedArguments Args; Args.Add(FText::FromString(AllFiles)); if (NumPrinted < TotalCount) { Args.Add(FText::AsNumber(TotalCount - NumPrinted)); RootText = LOCTEXT("DeleteSourceFilesAndMore_Tooltip", "When checked, the following source content files will also be deleted along with the assets:\n\n{0}\n... and {1} more."); } else { RootText = LOCTEXT("DeleteSourceFiles_Tooltip", "When checked, the following source content files will also be deleted along with the assets:\n\n{0}"); } return FText::Format(RootText, Args); } EVisibility SDeleteAssetsDialog::GetAssetReferencesVisiblity() const { return DeleteModel->GetAssetReferences().Num() == 0 ? EVisibility::Collapsed : EVisibility::Visible; } TSharedRef SDeleteAssetsDialog::BuildCantUseReplaceReferencesWidget() { return SNew( STextBlock ) .AutoWrapText( true ) .Text( LOCTEXT( "ReplaceReferencesNotAvailabeText", "Not all objects are compatible, so Replace References is unavailable." ) ); } TSharedRef SDeleteAssetsDialog::BuildReplaceReferencesWidget() { return SNew( SVerticalBox ) + SVerticalBox::Slot() .FillHeight( 1 ) .Padding( 0, 0, 0, 3 ) [ SNew( STextBlock ) .AutoWrapText( true ) //.Font( FAppStyle::GetFontStyle( "BoldFont" ) ) .Text( LOCTEXT( "ReplaceReferencesText", "Delete the assets and update referencers to point at an asset of your choosing." ) ) ] + SVerticalBox::Slot() .AutoHeight() [ SAssignNew( ConsolidationPickerComboButton, SComboButton ) .HAlign( EHorizontalAlignment::HAlign_Fill ) .VAlign( EVerticalAlignment::VAlign_Center ) .ComboButtonStyle( FAppStyle::Get(), "ToolbarComboButton" ) .ForegroundColor( FLinearColor::White ) .ContentPadding(3.0f) .MenuPlacement( EMenuPlacement::MenuPlacement_BelowAnchor ) .OnGetMenuContent( this, &SDeleteAssetsDialog::MakeConsolidationAssetPicker ) .ButtonContent() [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() [ CreateThumbnailWidget() ] + SHorizontalBox::Slot() .FillWidth( 1.0f ) .VAlign( VAlign_Center ) .Padding(5, 0) [ SNew( STextBlock ) .Text( this, &SDeleteAssetsDialog::GetConsolidateAssetName ) ] ] ] + SVerticalBox::Slot() .AutoHeight() [ SNew( SButton ) .HAlign( HAlign_Center ) .Text( LOCTEXT( "Replace References", "Replace References" ) ) .OnClicked( this, &SDeleteAssetsDialog::ReplaceReferences ) .ButtonStyle(FAppStyle::Get(), "FlatButton.Danger") .TextStyle(FAppStyle::Get(), "FlatButton.DefaultTextStyle") ]; } TSharedRef SDeleteAssetsDialog::BuildForceDeleteWidget() { return SNew( SVerticalBox ) + SVerticalBox::Slot() .FillHeight( 1.0f ) .Padding( 0, 0, 0, 3.0f ) [ SNew( STextBlock ) .AutoWrapText( true ) //.Font( FAppStyle::GetFontStyle( "BoldFont" ) ) .Text( LOCTEXT( "ForceDeleteText", "Delete the asset anyway, but referencers may not work correctly anymore.\n\nUse as a last resort." ) ) ] + SVerticalBox::Slot() .AutoHeight() [ SNew( SButton ) .HAlign( HAlign_Center ) .Text( LOCTEXT( "ForceDelete", "Force Delete" ) ) .ToolTipText( LOCTEXT( "ForceDeleteTooltipText", "Force Delete will obliterate all references to this asset and is dangerous.\n\nUse as a last resort." ) ) .ButtonStyle(FAppStyle::Get(), "FlatButton.Danger") .TextStyle(FAppStyle::Get(), "FlatButton.DefaultTextStyle") .OnClicked(this, &SDeleteAssetsDialog::ForceDelete) ]; } FReply SDeleteAssetsDialog::OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent ) { if( InKeyEvent.GetKey() == EKeys::Escape ) { if (TSharedPtr OwningWindow = ParentWindow.Pin()) { OwningWindow->RequestDestroyWindow(); } return FReply::Handled(); } if ( ReferencerCommands->ProcessCommandBindings(InKeyEvent) ) { return FReply::Handled(); } return FReply::Unhandled(); } EActiveTimerReturnType SDeleteAssetsDialog::TickDeleteModel( double InCurrentTime, float InDeltaTime ) { DeleteModel->Tick( InDeltaTime ); if ( DeleteModel->GetState() == FAssetDeleteModel::EState::Finished ) { bIsActiveTimerRegistered = false; return EActiveTimerReturnType::Stop; } return EActiveTimerReturnType::Continue; } void SDeleteAssetsDialog::HandleDeleteModelStateChanged(FAssetDeleteModel::EState NewState) { switch ( NewState ) { case FAssetDeleteModel::StartScanning: RootContainer->SetContent( BuildProgressDialog() ); break; case FAssetDeleteModel::Finished: RootContainer->SetContent( BuildDeleteDialog() ); break; case FAssetDeleteModel::Scanning: case FAssetDeleteModel::UpdateActions: case FAssetDeleteModel::Waiting: break; } } FText SDeleteAssetsDialog::ScanningText() const { return DeleteModel->GetProgressText(); } TOptional< float > SDeleteAssetsDialog::ScanningProgressFraction() const { return DeleteModel->GetProgress(); } TSharedRef SDeleteAssetsDialog::CreateThumbnailWidget() { ConsolidationAssetThumbnail = MakeShareable( new FAssetThumbnail( NULL, 40, 40, UThumbnailManager::Get().GetSharedThumbnailPool()) ); return SNew( SBox ) .WidthOverride( 40.0f ) .HeightOverride( 40.0f ) [ ConsolidationAssetThumbnail->MakeThumbnailWidget() ]; } EVisibility SDeleteAssetsDialog::GetReferencesVisiblity() const { return DeleteModel->IsAnythingReferencedInMemoryByNonUndo() ? EVisibility::Visible : EVisibility::Collapsed; } EVisibility SDeleteAssetsDialog::GetUndoVisiblity() const { return DeleteModel->IsAnythingReferencedInMemoryByUndo() ? EVisibility::Visible : EVisibility::Collapsed; } TSharedRef SDeleteAssetsDialog::HandleGenerateAssetRow( TSharedPtr InItem, const TSharedRef& OwnerTable ) { return SNew(SPendingDeleteRow, OwnerTable, InItem) .Visibility(InItem->IsInternal() ? EVisibility::Collapsed : EVisibility::Visible); } void SDeleteAssetsDialog::DeleteRelevantSourceContent() { if (DeleteModel->HasAnySourceContentFilesToDelete()) { auto* Settings = GetMutableDefault(); if (DeleteSourceFilesCheckbox->GetCheckedState() == ECheckBoxState::Checked) { Settings->bDeleteSourceFilesWithAssets = true; DeleteModel->DeleteSourceContentFiles(); } else { Settings->bDeleteSourceFilesWithAssets = false; } } } FReply SDeleteAssetsDialog::Delete() { if (TSharedPtr OwningWindow = ParentWindow.Pin()) { OwningWindow->RequestDestroyWindow(); } if (DeleteModel->IsAnythingReferencedInMemoryByUndo()) { GEditor->Trans->Reset(LOCTEXT("DeleteSelectedItem", "Delete Selected Item")); } DeleteRelevantSourceContent(); DeleteModel->DoDelete(); return FReply::Handled(); } FReply SDeleteAssetsDialog::Cancel() { if (TSharedPtr OwningWindow = ParentWindow.Pin()) { OwningWindow->RequestDestroyWindow(); } return FReply::Handled(); } FReply SDeleteAssetsDialog::ForceDelete() { if (TSharedPtr OwningWindow = ParentWindow.Pin()) { OwningWindow->RequestDestroyWindow(); } if( DeleteModel->IsAnythingReferencedInMemoryByUndo() ) { GEditor->Trans->Reset( LOCTEXT("DeleteSelectedItem", "Delete Selected Item") ); } DeleteRelevantSourceContent(); DeleteModel->DoForceDelete(); return FReply::Handled(); } FText SDeleteAssetsDialog::GetConsolidateAssetName() const { if ( !ConsolidationAsset.IsValid() ) { return LOCTEXT( "None", "None" ); } else { return FText::FromName(ConsolidationAsset.AssetName); } } FReply SDeleteAssetsDialog::ReplaceReferences() { if ( !ConsolidationAsset.IsValid() ) { return FReply::Handled(); } FText Message = FText::Format( LOCTEXT( "ReplaceMessage", "This will replace any reference to the pending deleted assets with {0}; and then delete them.\n\nAre you sure?" ), FText::FromName( ConsolidationAsset.AssetName ) ); FText Title = LOCTEXT( "ReplaceTitle", "Replace References?" ); if ( EAppReturnType::Ok == FMessageDialog::Open( EAppMsgType::OkCancel, Message, Title ) ) { if (TSharedPtr OwningWindow = ParentWindow.Pin()) { OwningWindow->RequestDestroyWindow(); } DeleteRelevantSourceContent(); DeleteModel->DoReplaceReferences( ConsolidationAsset ); } return FReply::Handled(); } TSharedRef SDeleteAssetsDialog::MakeAssetViewForReferencerAssets() { FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked(TEXT("ContentBrowser")); FAssetPickerConfig AssetPickerConfig; AssetPickerConfig.bAllowDragging = false; AssetPickerConfig.bCanShowClasses = false; AssetPickerConfig.bAllowNullSelection = false; AssetPickerConfig.bShowBottomToolbar = false; AssetPickerConfig.bAutohideSearchBar = true; AssetPickerConfig.AssetShowWarningText = TAttribute< FText >( this, &SDeleteAssetsDialog::GetReferencingAssetsEmptyText ); AssetPickerConfig.InitialAssetViewType = EAssetViewType::Tile; AssetPickerConfig.OnAssetsActivated = FOnAssetsActivated::CreateSP(this, &SDeleteAssetsDialog::OnAssetsActivated); AssetPickerConfig.OnShouldFilterAsset = FOnShouldFilterAsset::CreateSP(this, &SDeleteAssetsDialog::OnShouldFilterAsset); AssetPickerConfig.OnGetAssetContextMenu = FOnGetAssetContextMenu::CreateSP( this, &SDeleteAssetsDialog::OnGetAssetContextMenu ); AssetPickerConfig.GetCurrentSelectionDelegates.Add( &GetSelectedReferencerAssets ); return ContentBrowserModule.Get().CreateAssetPicker(AssetPickerConfig); } TSharedRef SDeleteAssetsDialog::MakeConsolidationAssetPicker() { FAssetPickerConfig AssetPickerConfig; //AssetPickerConfig.Filter.ClassPaths.Add( UStaticMesh::StaticClass()->GetFName() ); AssetPickerConfig.OnAssetSelected = FOnAssetSelected::CreateSP( this, &SDeleteAssetsDialog::OnAssetSelectedFromConsolidationPicker ); AssetPickerConfig.OnShouldFilterAsset = FOnShouldFilterAsset::CreateSP( this, &SDeleteAssetsDialog::OnShouldConsolidationFilterAsset ); AssetPickerConfig.bAllowNullSelection = false; AssetPickerConfig.InitialAssetViewType = EAssetViewType::List; AssetPickerConfig.bFocusSearchBoxWhenOpened = true; AssetPickerConfig.bShowBottomToolbar = true; AssetPickerConfig.bAllowDragging = false; AssetPickerConfig.bCanShowClasses = false; AssetPickerConfig.SelectionMode = ESelectionMode::Single; AssetPickerConfig.bAddFilterUI = true; FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked( TEXT( "ContentBrowser" ) ); return SNew( SBox ) .HeightOverride( 250.0f ) .WidthOverride( 300.0f ) [ ContentBrowserModule.Get().CreateAssetPicker( AssetPickerConfig ) ]; } FText SDeleteAssetsDialog::GetReferencingAssetsEmptyText() const { FString DiskReferences = "There Are Some Non-Displayable References\n\n"; static FName NAME_ActorLabel(TEXT("ActorLabel")); IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")).Get(); for ( const FName& DiskReference : DeleteModel->GetAssetReferences() ) { FString ReferenceToAppend; FARFilter Filter; Filter.PackageNames = { DiskReference }; TArray Assets; if (AssetRegistry.GetAssets(Filter, Assets)) { for (const FAssetData& Asset : Assets) { FString ActorLabel; if (Asset.GetTagValue(NAME_ActorLabel, ActorLabel)) { ReferenceToAppend = Asset.GetOptionalOuterPathName().ToString() + TEXT(".") + ActorLabel; break; } } } if (ReferenceToAppend.IsEmpty()) { ReferenceToAppend = DiskReference.ToString(); } DiskReferences += ReferenceToAppend + TEXT("\n"); } return FText::FromString( DiskReferences ); } /** Handler for when the user double clicks, presses enter, or presses space on an asset */ void SDeleteAssetsDialog::OnAssetsActivated(const TArray& ActivatedAssets, EAssetTypeActivationMethod::Type ActivationMethod) { // Open a simple asset editor for all assets which do not have asset type actions if activating with enter or double click if ( ActivationMethod == EAssetTypeActivationMethod::DoubleClicked || ActivationMethod == EAssetTypeActivationMethod::Opened ) { if (TSharedPtr OwningWindow = ParentWindow.Pin()) { OwningWindow->RequestDestroyWindow(); } for(const FAssetData& ActivatedAsset : ActivatedAssets) { FString MapFilePath; if (FEditorFileUtils::IsMapPackageAsset(ActivatedAsset.GetObjectPathString(), MapFilePath)) { if ( ActivatedAsset.IsAssetLoaded() ) { DeleteModel->GoToNextReferenceInLevel(); } else { if ( !GIsDemoMode ) { // If there are any unsaved changes to the current level, see if the user wants to save those first. bool bPromptUserToSave = true; bool bSaveMapPackages = true; bool bSaveContentPackages = true; if ( FEditorFileUtils::SaveDirtyPackages(bPromptUserToSave, bSaveMapPackages, bSaveContentPackages) == false ) { // something went wrong or the user pressed cancel. Return to the editor so the user doesn't lose their changes return; } } FEditorDirectories::Get().SetLastDirectory(ELastDirectory::LEVEL, FPaths::GetPath(MapFilePath)); FEditorFileUtils::LoadMap(MapFilePath, false, true); // @todo ndarnell Now that the map is loading how do i select the first reference... } } else { GEditor->GetEditorSubsystem()->OpenEditorForAsset(ActivatedAsset.GetAsset()); // @todo ndarnell select in content browser maybe as well? } } } } EVisibility SDeleteAssetsDialog::GetReplaceReferencesVisibility() const { // We can't replace references if nobody is referencing the pending deleted assets if ( DeleteModel->GetAssetReferences().Num() == 0 ) { return EVisibility::Collapsed; } return EVisibility::Visible; } EVisibility SDeleteAssetsDialog::GetForceDeleteVisibility() const { return CanForceDelete() ? EVisibility::Visible : EVisibility::Collapsed; } EVisibility SDeleteAssetsDialog::GetDeleteVisibility() const { return CanDelete() ? EVisibility::Visible : EVisibility::Collapsed; } EVisibility SDeleteAssetsDialog::GetDeleteSourceFilesVisibility() const { return DeleteModel->HasAnySourceContentFilesToDelete() ? EVisibility::Visible : EVisibility::Collapsed; } bool SDeleteAssetsDialog::CanReplaceReferences() const { return DeleteModel->CanReplaceReferences(); } bool SDeleteAssetsDialog::CanForceDelete() const { return DeleteModel->CanForceDelete(); } bool SDeleteAssetsDialog::CanDelete() const { return DeleteModel->CanDelete(); } bool SDeleteAssetsDialog::OnShouldConsolidationFilterAsset( const FAssetData& InAssetData ) const { return DeleteModel->CanReplaceReferencesWith( InAssetData ); } TSharedPtr SDeleteAssetsDialog::OnGetAssetContextMenu( const TArray& SelectedAssets ) { // Get all menu extenders for this context menu from the content browser module /*FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked( TEXT( "ContentBrowser" ) ); TArray MenuExtenderDelegates = ContentBrowserModule.GetAllAssetViewContextMenuExtenders(); TArray> Extenders; for ( int32 i = 0; i < MenuExtenderDelegates.Num(); ++i ) { if ( MenuExtenderDelegates[i].IsBound() ) { Extenders.Add( MenuExtenderDelegates[i].Execute( SelectedAssets ) ); } }*/ //TSharedPtr MenuExtender = FExtender::Combine( Extenders ); FMenuBuilder MenuBuilder(true, ReferencerCommands); MenuBuilder.BeginSection("AssetOptions", LOCTEXT("AssetOptionsText", "Asset Options")); { MenuBuilder.AddMenuEntry( FGenericCommands::Get().Delete, NAME_None, LOCTEXT( "AddPendingDelete", "Add to Pending Deletes" ), LOCTEXT( "AddPendingDeleteTooltip", "Adds the selected assets to the list of pending deleted assets." ) ); } MenuBuilder.EndSection(); return MenuBuilder.MakeWidget(); } bool SDeleteAssetsDialog::CanExecuteDeleteReferencers() const { TArray SelectedAssets = GetSelectedReferencerAssets.Execute(); return SelectedAssets.Num() > 0; } void SDeleteAssetsDialog::ExecuteDeleteReferencers() { TArray SelectedAssets = GetSelectedReferencerAssets.Execute(); for ( const FAssetData& SelectedAsset : SelectedAssets ) { UObject* ObjectToDelete = SelectedAsset.GetAsset(); if (ObjectToDelete) { DeleteModel->AddObjectToDelete(ObjectToDelete); } if ( !bIsActiveTimerRegistered ) { bIsActiveTimerRegistered = true; RegisterActiveTimer( 0.f, FWidgetActiveTimerDelegate::CreateSP( this, &SDeleteAssetsDialog::TickDeleteModel ) ); } } } bool SDeleteAssetsDialog::OnShouldFilterAsset( const FAssetData& InAssetData ) const { // Filter out any redirectors that are not to the main UAsset if ( InAssetData.IsRedirector() && !InAssetData.IsUAsset() ) { return true; } // If it's in the set of references then don't filter it. if ( DeleteModel->GetAssetReferences().Contains( InAssetData.PackageName ) ) { return false; } return true; } void SDeleteAssetsDialog::OnAssetSelectedFromConsolidationPicker(const FAssetData& AssetData) { ConsolidationAssetThumbnail->SetAsset( AssetData ); ConsolidationAssetThumbnail->RefreshThumbnail(); ConsolidationAsset = AssetData; ConsolidationPickerComboButton->SetIsOpen( false ); } ////////////////////////////////////////////////////////////////////////// #undef LOCTEXT_NAMESPACE