feat: asset deletion scan and delete dialog (WIP)
Adds AssetDeletionScan to Core (no UI/editor-pipeline deps, invariant holds) and SAssetDeleteDialog to the Editor module, plus 10 spec cases. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include "AssetUsageExporter.h"
|
||||
#include "AssetUsagePaths.h"
|
||||
#include "AssetUsageReportWriter.h"
|
||||
#include "SAssetDeleteDialog.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "AssetRegistry/IAssetRegistry.h"
|
||||
#include "Algo/AllOf.h"
|
||||
@@ -580,6 +581,33 @@ void SAssetUsageAuditPanel::Construct(const FArguments& InArgs)
|
||||
.OnClicked(this, &SAssetUsageAuditPanel::OnExportFilesClicked)
|
||||
.IsEnabled(this, &SAssetUsageAuditPanel::HasCheckedAssets)
|
||||
]
|
||||
|
||||
// Delete sits last and behind a separator, deliberately. It acts on the same ticked
|
||||
// set as Export, so it belongs on this bar, but it is the only irreversible action in
|
||||
// the tool and must never be the button someone reaches by muscle memory.
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.VAlign(VAlign_Center)
|
||||
.Padding(8.0f, 2.0f, 2.0f, 2.0f)
|
||||
[
|
||||
SNew(SSeparator)
|
||||
.Orientation(Orient_Vertical)
|
||||
]
|
||||
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.VAlign(VAlign_Center)
|
||||
.Padding(2.0f, 0.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("DeleteTicked", "Delete ticked assets..."))
|
||||
.ToolTipText(LOCTEXT("DeleteTickedTip",
|
||||
"Permanently delete the ticked assets from the project. Levels are never deleted. "
|
||||
"Shows what still references them before anything is removed."))
|
||||
.ButtonColorAndOpacity(FLinearColor(0.55f, 0.16f, 0.16f))
|
||||
.OnClicked(this, &SAssetUsageAuditPanel::OnDeleteClicked)
|
||||
.IsEnabled(this, &SAssetUsageAuditPanel::HasCheckedAssets)
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
@@ -2224,6 +2252,83 @@ FReply SAssetUsageAuditPanel::OnExportFilesClicked()
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
// --- Deleting ----------------------------------------------------------------------------------
|
||||
|
||||
FReply SAssetUsageAuditPanel::OnDeleteClicked()
|
||||
{
|
||||
TArray<FAssetUsageRow> Rows;
|
||||
GetCheckedRows(Rows);
|
||||
|
||||
if (Rows.IsEmpty())
|
||||
{
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
TArray<FName> Packages;
|
||||
Packages.Reserve(Rows.Num());
|
||||
|
||||
for (const FAssetUsageRow& Row : Rows)
|
||||
{
|
||||
Packages.Add(Row.PackageName);
|
||||
}
|
||||
|
||||
// Deliberately NOT widened with the dependency closure, unlike export. Export widens so that a
|
||||
// mesh arrives with its materials; widening a delete the same way would destroy content nobody
|
||||
// ticked, and a single shared texture would drag half the project along with it. Delete acts on
|
||||
// exactly what was ticked, and never on more.
|
||||
const FAssetDeleteOutcome Outcome = ShowAssetDeleteDialog(Packages);
|
||||
|
||||
if (Outcome.bCancelled)
|
||||
{
|
||||
StatusText = LOCTEXT("DeleteCancelled", "Delete cancelled - nothing was changed.");
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
ForgetDeletedPackages(Outcome.DeletedPackages);
|
||||
|
||||
StatusText = FText::Format(
|
||||
LOCTEXT("DeleteFinished",
|
||||
"Deleted {0} of {1} ticked asset(s); {2} were refused. Reference counts elsewhere in this result are now out of date - run the audit again."),
|
||||
FText::AsNumber(Outcome.DeletedPackages.Num()),
|
||||
FText::AsNumber(Outcome.Requested),
|
||||
FText::AsNumber(Outcome.Refused));
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
void SAssetUsageAuditPanel::ForgetDeletedPackages(const TArray<FName>& DeletedPackages)
|
||||
{
|
||||
if (DeletedPackages.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const TSet<FName> Gone(DeletedPackages);
|
||||
|
||||
LastResult.Rows.RemoveAll([&Gone](const FAssetUsageRow& Row)
|
||||
{
|
||||
return Gone.Contains(Row.PackageName);
|
||||
});
|
||||
|
||||
AllRows.RemoveAll([&Gone](const FAssetUsageRowPtr& Row)
|
||||
{
|
||||
return Row.IsValid() && Gone.Contains(Row->PackageName);
|
||||
});
|
||||
|
||||
for (FName Package : DeletedPackages)
|
||||
{
|
||||
CheckedPackages.Remove(Package);
|
||||
}
|
||||
|
||||
// Removing the rows is honest but incomplete: everything that referenced them still carries the
|
||||
// reference counts it had before, and an asset that was UsedByAssetsOnly through a now-deleted
|
||||
// Blueprint has a different verdict than the one on screen. Marking the result stale says so,
|
||||
// rather than leaving figures that look freshly measured.
|
||||
bResultStale = true;
|
||||
|
||||
RebuildFilteredRows();
|
||||
}
|
||||
|
||||
void SAssetUsageAuditPanel::RunMigrateExport(const TArray<FAssetUsageRow>& Rows, const FAssetExportRequest& Request)
|
||||
{
|
||||
TArray<FName> Packages;
|
||||
|
||||
Reference in New Issue
Block a user