Files
UnrealEngine/Engine/Source/Editor/UnrealEd/Private/Dialogs/SPrivateAssetsDialog.cpp
2025-05-18 13:04:45 +08:00

453 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Dialogs/SPrivateAssetsDialog.h"
#include "Widgets/Notifications/SProgressBar.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Views/SListView.h"
#include "SPrimaryButton.h"
#include "SWarningOrErrorBox.h"
#include "IContentBrowserSingleton.h"
#include "ContentBrowserModule.h"
#include "Editor.h"
#include "Editor/Transactor.h"
#define LOCTEXT_NAMESPACE "SPrivateAssetsDialog"
namespace PrivatizeAssetsView
{
static const FName ColumnID_Asset("Asset");
static const FName ColumnID_AssetClass("Class");
static const FName ColumnID_DiskReferences("DiskReferences");
static const FName ColumnID_MemoryReferences("MemoryReferences");
}
//////////////////////////////////////////////////////////////////////////
// SPendingPrivateAssetRow
class SPendingPrivateAssetRow : public SMultiColumnTableRow<TSharedPtr<FPendingPrivateAsset>>
{
public:
SLATE_BEGIN_ARGS(SPendingPrivateAssetRow) {}
SLATE_END_ARGS()
void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, TSharedPtr<FPendingPrivateAsset> InItem)
{
Item = InItem;
SMultiColumnTableRow<TSharedPtr<FPendingPrivateAsset>>::Construct(FSuperRowType::FArguments(), InOwnerTableView);
}
virtual TSharedRef<SWidget> GenerateWidgetForColumn(const FName& ColumnName)
{
if (ColumnName == PrivatizeAssetsView::ColumnID_Asset)
{
return SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(3, 0, 0, 0)
[
SNew(STextBlock)
.Text(FText::FromString(Item->GetObject()->GetName()))
];
}
else if (ColumnName == PrivatizeAssetsView::ColumnID_AssetClass)
{
return SNew(STextBlock)
.Text(FText::FromString(Item->GetObject()->GetClass()->GetName()));
}
else if (ColumnName == PrivatizeAssetsView::ColumnID_DiskReferences)
{
int32 IllegalDiskReferenceCount = Item->IllegalDiskReferences.Num();
FFormatNamedArguments Args;
Args.Add(TEXT("AssetCount"), FText::AsNumber(IllegalDiskReferenceCount));
FText OnDiskCountText = IllegalDiskReferenceCount > 1 ?
FText::Format(LOCTEXT("InAssetReferences", "{AssetCount} References"), Args) : FText::Format(LOCTEXT("InAssetReference", "{AssetCount} Reference"), Args);
return SNew(STextBlock)
.Text(OnDiskCountText)
.Visibility(IllegalDiskReferenceCount > 0 ? EVisibility::Visible : EVisibility::Hidden);
}
else if (ColumnName == PrivatizeAssetsView::ColumnID_MemoryReferences)
{
int32 IllegalMemoryReferenceCount = Item->IllegalMemoryReferences.ExternalReferences.Num();
FFormatNamedArguments Args;
Args.Add(TEXT("ReferenceCount"), FText::AsNumber(IllegalMemoryReferenceCount));
FText InMemoryCountText = IllegalMemoryReferenceCount > 1 ?
FText::Format(LOCTEXT("InMemoryReferences", "{ReferenceCount} References"), Args) : FText::Format(LOCTEXT("InMemoryReference", "{ReferenceCount} Reference"), Args);
return SNew(STextBlock)
.Text(InMemoryCountText)
.Visibility(IllegalMemoryReferenceCount > 0 ? EVisibility::Visible : EVisibility::Hidden);
}
return SNullWidget::NullWidget;
}
private:
TSharedPtr<FPendingPrivateAsset> Item;
};
//////////////////////////////////////////////////////////////////////////
// SPrivateAssetsDialog
SPrivateAssetsDialog::~SPrivateAssetsDialog()
{
PrivatizeModel->OnStateChanged().RemoveAll(this);
}
void SPrivateAssetsDialog::Construct(const FArguments& InArgs, const TSharedRef<FAssetPrivatizeModel> InPrivatizeModel)
{
bIsActiveTimerRegistered = true;
RegisterActiveTimer(0.0f, FWidgetActiveTimerDelegate::CreateSP(this, &SPrivateAssetsDialog::TickPrivatizeModel));
PrivatizeModel = InPrivatizeModel;
ParentWindow = InArgs._ParentWindow;
ChildSlot
[
SAssignNew(RootContainer, SBorder)
.BorderImage(FAppStyle::GetBrush("AssetDeleteDialog.Background"))
.Padding(10)
];
PrivatizeModel->OnStateChanged().AddRaw(this, &SPrivateAssetsDialog::HandlePrivatizeModelStateChanged);
HandlePrivatizeModelStateChanged(PrivatizeModel->GetState());
}
FReply SPrivateAssetsDialog::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{
if (InKeyEvent.GetKey() == EKeys::Escape)
{
ParentWindow.Get()->RequestDestroyWindow();
return FReply::Handled();
}
return FReply::Unhandled();
}
TSharedRef<SWidget> SPrivateAssetsDialog::BuildProgressDialog()
{
// Shows a progress bar during the scanning of references for each pending private asset
return SNew(SVerticalBox)
+ SVerticalBox::Slot()
.VAlign(VAlign_Center)
.FillHeight(1.0f)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.Padding(5.0f, 0)
[
SNew(STextBlock)
.Text(this, &SPrivateAssetsDialog::ScanningText)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(5.0f, 10.0f)
[
SNew(SProgressBar)
.Percent(this, &SPrivateAssetsDialog::ScanningProgressFraction)
]
];
}
TSharedRef<SWidget> SPrivateAssetsDialog::BuildPrivatizeDialog()
{
TSharedRef<SHeaderRow> HeaderRowWidget =
SNew(SHeaderRow)
+SHeaderRow::Column(PrivatizeAssetsView::ColumnID_Asset)
.DefaultLabel(LOCTEXT("Column_AssetName", "Asset"))
.HAlignHeader(EHorizontalAlignment::HAlign_Left)
.FillWidth(0.5f)
+SHeaderRow::Column(PrivatizeAssetsView::ColumnID_AssetClass)
.DefaultLabel(LOCTEXT("Column_AssetClass", "Class"))
.HAlignHeader(HAlign_Left)
.FillWidth(0.25f)
+SHeaderRow::Column(PrivatizeAssetsView::ColumnID_DiskReferences)
.DefaultLabel(LOCTEXT("Column_DiskReferences", "Asset Referencers"))
.HAlignHeader(HAlign_Left)
.FillWidth(0.25f)
+SHeaderRow::Column(PrivatizeAssetsView::ColumnID_MemoryReferences)
.DefaultLabel(LOCTEXT("Column_MemoryReferences", "Memory Referencers"))
.HAlignHeader(HAlign_Left)
.FillWidth(0.25f);
return SNew(SVerticalBox)
// The to be made private assets and their reference counts
+SVerticalBox::Slot()
.FillHeight(0.5f)
.Padding(5.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(FMargin(0, 0, 0, 3))
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("DetailsView.CategoryTop"))
.BorderBackgroundColor(FLinearColor(.6f, .6f, .6f, 1.0f))
.Padding(3.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("AttemptingPrivatize", "The following assets will be made private:"))
.Font(FAppStyle::GetFontStyle("BoldFont"))
]
]
+SVerticalBox::Slot()
.FillHeight(1.0f)
[
SAssignNew(ObjectsToPrivatizeList, SListView<TSharedPtr<FPendingPrivateAsset>>)
.ListItemsSource(PrivatizeModel->GetPendingPrivateAssets())
.OnGenerateRow(this, &SPrivateAssetsDialog::HandleGenerateAssetRow)
.HeaderRow(HeaderRowWidget)
]
]
]
// An asset list of all on disk references that would become illegal if the pending assets are made private
+SVerticalBox::Slot()
.FillHeight(1.0f)
.Padding(5.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("Brushes.Panel"))
.Visibility(this, &SPrivateAssetsDialog::GetAssetReferencesVisibility)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("DetailsView.CategoryTop"))
.BorderBackgroundColor(FLinearColor(.6f, .6f, .6f, 1.0f))
.Padding(3.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("AssetsReferencingPendingPrivateAssets", "References to the above assets will be removed from the following:"))
.Font(FAppStyle::GetFontStyle("BoldFont"))
]
]
+SVerticalBox::Slot()
.FillHeight(1.0f)
[
BuildAssetViewForReferencerAssets()
]
]
]
// Details any extra warning information about the operation
+SVerticalBox::Slot()
.AutoHeight()
.Padding(FMargin(8.0f, 5.0f))
[
SNew(SWarningOrErrorBox)
.Visibility(this, &SPrivateAssetsDialog::GetWarningTextVisibility)
.MessageStyle(EMessageStyle::Warning)
.Message(this, &SPrivateAssetsDialog::GetWarningText)
]
// Confirm or cancel buttons
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("Brushes.Panel"))
.Padding(0)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 4)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(1.0)
.Padding(6, 0)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("NoBorder"))
[
SNew(SPrimaryButton)
.Text(LOCTEXT("Privatize", "Make Private"))
.ToolTipText(LOCTEXT("PrivatizeTooltipText", "Make private"))
.OnClicked(this, &SPrivateAssetsDialog::ForcePrivatize)
]
]
+SHorizontalBox::Slot()
.FillWidth(1.0)
.Padding(6, 0)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("NoBorder"))
.VAlign(VAlign_Bottom)
[
SNew(SButton)
.HAlign(HAlign_Center)
.Text(LOCTEXT("Cancel", "Cancel"))
.ToolTipText(LOCTEXT("CancelPrivatizeTooltipText", "Cancel making selection private"))
.OnClicked(this, &SPrivateAssetsDialog::Cancel)
]
]
]
]
];
}
TSharedRef<SWidget> SPrivateAssetsDialog::BuildAssetViewForReferencerAssets()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
FAssetPickerConfig AssetPickerConfig;
AssetPickerConfig.bAllowDragging = false;
AssetPickerConfig.bCanShowClasses = false;
AssetPickerConfig.bAllowNullSelection = false;
AssetPickerConfig.bShowBottomToolbar = false;
AssetPickerConfig.bAutohideSearchBar = true;
AssetPickerConfig.InitialAssetViewType = EAssetViewType::Column;
AssetPickerConfig.OnShouldFilterAsset = FOnShouldFilterAsset::CreateSP(this, &SPrivateAssetsDialog::OnShouldFilterAsset);
return ContentBrowserModule.Get().CreateAssetPicker(AssetPickerConfig);
}
EActiveTimerReturnType SPrivateAssetsDialog::TickPrivatizeModel(double InCurrentTime, float InDeltaTime)
{
PrivatizeModel->Tick(InDeltaTime);
if (PrivatizeModel->GetState() == FAssetPrivatizeModel::EState::Finished)
{
bIsActiveTimerRegistered = false;
return EActiveTimerReturnType::Stop;
}
return EActiveTimerReturnType::Continue;
}
void SPrivateAssetsDialog::HandlePrivatizeModelStateChanged(FAssetPrivatizeModel::EState NewState)
{
switch (NewState)
{
case FAssetPrivatizeModel::StartScanning:
RootContainer->SetContent(BuildProgressDialog());
break;
case FAssetPrivatizeModel::Finished:
// If in our scan we realize we can cleanly perform the privatize operation without any illegal reference fix up
// We can skip the confirmation dialog and close down
if (PrivatizeModel->CanPrivatize())
{
Privatize();
}
RootContainer->SetContent(BuildPrivatizeDialog());
break;
default:
break;
}
}
bool SPrivateAssetsDialog::OnShouldFilterAsset(const FAssetData& InAssetData) const
{
if (InAssetData.IsRedirector() && !InAssetData.IsUAsset())
{
return true;
}
if (PrivatizeModel->GetIllegalAssetReferences().Contains(InAssetData.PackageName))
{
return false;
}
return true;
}
FReply SPrivateAssetsDialog::Privatize()
{
ParentWindow.Get()->RequestDestroyWindow();
PrivatizeModel->DoPrivatize();
return FReply::Handled();
}
FReply SPrivateAssetsDialog::ForcePrivatize()
{
ParentWindow.Get()->RequestDestroyWindow();
// If anything is referenced in undo we need to purge the undo stack
// Otherwise you could undo back to restoring a reference which would now be illegal
if (PrivatizeModel->IsAnythingReferencedInMemoryByUndo())
{
GEditor->Trans->Reset(LOCTEXT("PrivatizeSelectedItem", "Privatize Selected Item"));
}
PrivatizeModel->DoForcePrivatize();
return FReply::Handled();
}
FReply SPrivateAssetsDialog::Cancel()
{
ParentWindow.Get()->RequestDestroyWindow();
return FReply::Handled();
}
FText SPrivateAssetsDialog::GetWarningText() const
{
bool bReferencesFoundInUndo = PrivatizeModel->IsAnythingReferencedInMemoryByUndo();
bool bReferencesFoundInNonUndo = PrivatizeModel->IsAnythingReferencedInMemoryByNonUndo();
FText MemoryWarning = LOCTEXT("PrivateAssetMemoryReferenceWarning", "- Some assets are still referenced in memory");
FText UndoWarning = LOCTEXT("PrivateAssetUndoWarning", "- Undo stack will be cleared as a result of this action");
FText WarningText;
if (bReferencesFoundInNonUndo)
{
WarningText = MemoryWarning;
}
if (bReferencesFoundInUndo)
{
WarningText = FText::Join(FText::FromString("\n"), WarningText, UndoWarning);
}
return WarningText;
}
EVisibility SPrivateAssetsDialog::GetWarningTextVisibility() const
{
return (PrivatizeModel->IsAnythingReferencedInMemoryByUndo() ||
PrivatizeModel->IsAnythingReferencedInMemoryByNonUndo()) ? EVisibility::Visible : EVisibility::Collapsed;
}
EVisibility SPrivateAssetsDialog::GetAssetReferencesVisibility() const
{
return PrivatizeModel->GetIllegalAssetReferences().Num() == 0 ? EVisibility::Collapsed : EVisibility::Visible;
}
FText SPrivateAssetsDialog::ScanningText() const
{
return PrivatizeModel->GetProgressText();
}
TOptional<float> SPrivateAssetsDialog::ScanningProgressFraction() const
{
return PrivatizeModel->GetProgress();
}
TSharedRef<ITableRow> SPrivateAssetsDialog::HandleGenerateAssetRow(TSharedPtr<FPendingPrivateAsset> InItem, const TSharedRef<STableViewBase>& OwnerTable)
{
return SNew(SPendingPrivateAssetRow, OwnerTable, InItem);
}
#undef LOCTEXT_NAMESPACE