633 lines
20 KiB
C++
633 lines
20 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "PathContextMenu.h"
|
|
|
|
#include "AssetViewUtils.h"
|
|
#include "Containers/ArrayView.h"
|
|
#include "Containers/Map.h"
|
|
#include "Containers/StringFwd.h"
|
|
#include "Containers/StringView.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "ContentBrowserCommands.h"
|
|
#include "ContentBrowserDataFilter.h"
|
|
#include "ContentBrowserDataSource.h"
|
|
#include "ContentBrowserDataSubsystem.h"
|
|
#include "ContentBrowserDelegates.h"
|
|
#include "ContentBrowserMenuContexts.h"
|
|
#include "ContentBrowserModule.h"
|
|
#include "ContentBrowserSingleton.h"
|
|
#include "ContentBrowserStyle.h"
|
|
#include "ContentBrowserUtils.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Framework/Commands/GenericCommands.h"
|
|
#include "Framework/Commands/UIAction.h"
|
|
#include "Framework/Commands/UICommandInfo.h"
|
|
#include "Framework/MultiBox/MultiBoxExtender.h"
|
|
#include "Framework/SlateDelegates.h"
|
|
#include "HAL/IConsoleManager.h"
|
|
#include "HAL/PlatformApplicationMisc.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "IContentBrowserDataModule.h"
|
|
#include "Math/Vector2D.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Misc/Attribute.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "SlotBase.h"
|
|
#include "SourceControlOperations.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Templates/Casts.h"
|
|
#include "Templates/Tuple.h"
|
|
#include "Templates/UnrealTemplate.h"
|
|
#include "Textures/SlateIcon.h"
|
|
#include "ToolMenu.h"
|
|
#include "ToolMenuDelegates.h"
|
|
#include "ToolMenuEntry.h"
|
|
#include "ToolMenuSection.h"
|
|
#include "ToolMenus.h"
|
|
#include "UObject/NameTypes.h"
|
|
#include "UObject/UnrealNames.h"
|
|
#include "Widgets/Colors/SColorBlock.h"
|
|
#include "Widgets/Colors/SColorPicker.h"
|
|
#include "Widgets/DeclarativeSyntaxSupport.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/SBoxPanel.h"
|
|
#include "Widgets/SWidget.h"
|
|
#include "Widgets/SWindow.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "ContentBrowser"
|
|
|
|
FPathContextMenu::FPathContextMenu(const TWeakPtr<SWidget>& InParentContent)
|
|
: ParentContent(InParentContent)
|
|
{
|
|
}
|
|
|
|
void FPathContextMenu::SetOnRenameFolderRequested(const FOnRenameFolderRequested& InOnRenameFolderRequested)
|
|
{
|
|
OnRenameFolderRequested = InOnRenameFolderRequested;
|
|
}
|
|
|
|
void FPathContextMenu::SetOnFolderDeleted(const FOnFolderDeleted& InOnFolderDeleted)
|
|
{
|
|
OnFolderDeleted = InOnFolderDeleted;
|
|
}
|
|
|
|
void FPathContextMenu::SetOnFolderFavoriteToggled(const FOnFolderFavoriteToggled& InOnFolderFavoriteToggled)
|
|
{
|
|
OnFolderFavoriteToggled = InOnFolderFavoriteToggled;
|
|
}
|
|
|
|
void FPathContextMenu::SetOnPrivateContentEditToggled(const FOnPrivateContentEditToggled& InOnPrivateContentEditToggled)
|
|
{
|
|
OnPrivateContentEditToggled = InOnPrivateContentEditToggled;
|
|
}
|
|
|
|
const TArray<FContentBrowserItem>& FPathContextMenu::GetSelectedFolders() const
|
|
{
|
|
return SelectedFolders;
|
|
}
|
|
|
|
void FPathContextMenu::SetSelectedFolders(const TArray<FContentBrowserItem>& InSelectedFolders)
|
|
{
|
|
SelectedFolders = InSelectedFolders;
|
|
}
|
|
|
|
TSharedRef<FExtender> FPathContextMenu::MakePathViewContextMenuExtender(const TArray<FString>& InSelectedPaths)
|
|
{
|
|
// Get all menu extenders for this context menu from the content browser module
|
|
FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>( TEXT("ContentBrowser") );
|
|
TArray<FContentBrowserMenuExtender_SelectedPaths> MenuExtenderDelegates = ContentBrowserModule.GetAllPathViewContextMenuExtenders();
|
|
|
|
TArray<TSharedPtr<FExtender>> Extenders;
|
|
for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
|
|
{
|
|
if (MenuExtenderDelegates[i].IsBound())
|
|
{
|
|
Extenders.Add(MenuExtenderDelegates[i].Execute( InSelectedPaths ));
|
|
}
|
|
}
|
|
TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);
|
|
return MenuExtender.ToSharedRef();
|
|
}
|
|
|
|
void FPathContextMenu::MakePathViewContextMenu(UToolMenu* Menu)
|
|
{
|
|
UContentBrowserFolderContext* Context = Menu->FindContext<UContentBrowserFolderContext>();
|
|
|
|
// Only add something if at least one folder is selected
|
|
if ( SelectedFolders.Num() > 0 )
|
|
{
|
|
// Common operations section //
|
|
{
|
|
FToolMenuSection& Section = Menu->AddSection("PathViewFolderOptions", LOCTEXT("PathViewOptionsMenuHeading", "Folder Options") );
|
|
|
|
{
|
|
FText NewAssetToolTip;
|
|
if(SelectedFolders.Num() == 1)
|
|
{
|
|
if(Context->bCanBeModified)
|
|
{
|
|
NewAssetToolTip = FText::Format(LOCTEXT("NewAssetTooltip_CreateIn", "Create a new item in {0}."), FText::FromName(SelectedFolders[0].GetVirtualPath()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NewAssetToolTip = LOCTEXT("NewAssetTooltip_InvalidNumberOfPaths", "Can only create items when there is a single path selected.");
|
|
}
|
|
|
|
// New Asset (submenu)
|
|
if (Context->bCanBeModified)
|
|
{
|
|
FToolMenuEntry& NewAssetEntry = Section.AddSubMenu(
|
|
"NewAsset",
|
|
LOCTEXT("AddImportLabel", "Add/Import Content"),
|
|
NewAssetToolTip,
|
|
FNewToolMenuDelegate::CreateRaw(this, &FPathContextMenu::MakeNewAssetSubMenu),
|
|
FUIAction(),
|
|
EUserInterfaceActionType::Button,
|
|
false,
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Import")
|
|
);
|
|
|
|
NewAssetEntry.SubMenuData.Style.StyleSet = &UE::ContentBrowser::Private::FContentBrowserStyle::Get();
|
|
}
|
|
}
|
|
|
|
// Explore
|
|
if (!Context->bNoFolderOnDisk)
|
|
{
|
|
Section.AddMenuEntry(
|
|
"Explore",
|
|
ContentBrowserUtils::GetExploreFolderText(),
|
|
LOCTEXT("ExploreTooltip", "Finds this folder on disk."),
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.BrowseContent"),
|
|
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteExplore ) )
|
|
);
|
|
}
|
|
|
|
// Assume paths with an on-disk representation also have an internal path to copy
|
|
if (!Context->bNoFolderOnDisk)
|
|
{
|
|
Section.AddMenuEntry(
|
|
"CopyPath",
|
|
LOCTEXT("CopyFolderPath", "Copy Path"),
|
|
LOCTEXT("CopyFolderTooltip", "Copy the paths of the selected folder(s)"),
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "GenericCommands.Copy"),
|
|
FExecuteAction::CreateSP(this, &FPathContextMenu::CopySelectedFolder)
|
|
);
|
|
}
|
|
|
|
if (Context->bCanBeModified)
|
|
{
|
|
Section.AddMenuEntry(FGenericCommands::Get().Rename,
|
|
LOCTEXT("RenameFolder", "Rename"),
|
|
LOCTEXT("RenameFolderTooltip", "Rename the selected folder.")
|
|
);
|
|
}
|
|
|
|
// If any colors have already been set, display color options as a sub menu
|
|
if ( ContentBrowserUtils::HasCustomColors() )
|
|
{
|
|
// Set Color (submenu)
|
|
Section.AddSubMenu(
|
|
"SetColor",
|
|
LOCTEXT("SetColor", "Set Color"),
|
|
LOCTEXT("SetColorTooltip", "Sets the color this folder should appear as."),
|
|
FNewToolMenuDelegate::CreateRaw( this, &FPathContextMenu::MakeSetColorSubMenu ),
|
|
false,
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Color")
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Set Color
|
|
Section.AddMenuEntry(
|
|
"SetColor",
|
|
LOCTEXT("SetColor", "Set Color"),
|
|
LOCTEXT("SetColorTooltip", "Sets the color this folder should appear as."),
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Color"),
|
|
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecutePickColor ) )
|
|
);
|
|
}
|
|
|
|
FString SelectedFolderPath = SelectedFolders[0].GetVirtualPath().ToString();
|
|
FContentBrowserItemPath SelectedFolderItemPath(SelectedFolders[0].GetVirtualPath(), EContentBrowserPathType::Virtual);
|
|
// If this folder is already favorited, show the option to remove from favorites
|
|
if (ContentBrowserUtils::IsFavoriteFolder(SelectedFolderItemPath))
|
|
{
|
|
// Remove from favorites
|
|
Section.AddMenuEntry(
|
|
"RemoveFromFavorites",
|
|
LOCTEXT("RemoveFromFavorites", "Remove From Favorites"),
|
|
LOCTEXT("RemoveFromFavoritesTooltip", "Removes this folder from the favorites section."),
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "PropertyWindow.Favorites_Disabled"),
|
|
FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteFavorite))
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Add to favorites
|
|
Section.AddMenuEntry(
|
|
"AddToFavorites",
|
|
LOCTEXT("AddToFavorites", "Add To Favorites"),
|
|
LOCTEXT("AddToFavoritesTooltip", "Adds this folder to the favorites section for easy access."),
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Star"),
|
|
FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteFavorite))
|
|
);
|
|
}
|
|
|
|
static const IConsoleVariable* EnablePublicAssetFeatureCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("AssetTools.EnablePublicAssetFeature"));
|
|
const bool bIsPublicAssetUIEnabled = EnablePublicAssetFeatureCVar && EnablePublicAssetFeatureCVar->GetBool();
|
|
|
|
FStringView SelectedFolderPathView(SelectedFolderPath);
|
|
if (bIsPublicAssetUIEnabled && FContentBrowserSingleton::Get().IsFolderShowPrivateContentToggleable(SelectedFolderPathView))
|
|
{
|
|
if (FContentBrowserSingleton::Get().IsShowingPrivateContent(SelectedFolderPathView))
|
|
{
|
|
Section.AddMenuEntry(
|
|
"DisallowPrivateContentEditing",
|
|
LOCTEXT("DisallowPrivateContentEditing", "Disallow Private Content Editing"),
|
|
LOCTEXT("DisallowPrivateContentEditingTooltip", "Hides Private Content and prevents editing the Public/Private state of content in this folder"),
|
|
FSlateIcon(UE::ContentBrowser::Private::FContentBrowserStyle::Get().GetStyleSetName(), "ContentBrowser.PrivateContentEdit"),
|
|
FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecutePrivateContentEdit))
|
|
);
|
|
}
|
|
else
|
|
{
|
|
Section.AddMenuEntry(
|
|
"AllowPrivateContentEditing",
|
|
LOCTEXT("AllowPrivateContentEditing", "Allow Private Content Editing"),
|
|
LOCTEXT("AllowPrivateContentEditingTooltip", "Reveals Private Content and allows editing the Public/Private state of content in this folder"),
|
|
FSlateIcon(UE::ContentBrowser::Private::FContentBrowserStyle::Get().GetStyleSetName(), "ContentBrowser.PrivateContentEdit"),
|
|
FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecutePrivateContentEdit))
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Context->bCanBeModified)
|
|
{
|
|
// Bulk operations section //
|
|
{
|
|
FToolMenuSection& Section = Menu->AddSection("PathContextBulkOperations", LOCTEXT("AssetTreeBulkMenuHeading", "Bulk Operations") );
|
|
|
|
// Save
|
|
Section.AddMenuEntry(FContentBrowserCommands::Get().SaveAllCurrentFolder,
|
|
LOCTEXT("SaveFolder", "Save All"),
|
|
LOCTEXT("SaveFolderTooltip", "Saves all modified assets in this folder.")
|
|
);
|
|
|
|
// Resave
|
|
Section.AddMenuEntry(FContentBrowserCommands::Get().ResaveAllCurrentFolder);
|
|
|
|
// Delete
|
|
Section.AddMenuEntry(FGenericCommands::Get().Delete,
|
|
LOCTEXT("DeleteFolder", "Delete"),
|
|
TAttribute<FText>::Create(TAttribute<FText>::FGetter::CreateSP(this, &FPathContextMenu::GetDeleteToolTip))
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPathContextMenu::MakeNewAssetSubMenu(UToolMenu* Menu)
|
|
{
|
|
UToolMenus::Get()->AssembleMenuHierarchy(Menu, UToolMenus::Get()->CollectHierarchy("ContentBrowser.AddNewContextMenu"));
|
|
}
|
|
|
|
void FPathContextMenu::MakeSetColorSubMenu(UToolMenu* Menu)
|
|
{
|
|
{
|
|
FToolMenuSection& Section = Menu->AddSection("Section");
|
|
|
|
// New Color
|
|
Section.AddMenuEntry(
|
|
"NewColor",
|
|
LOCTEXT("NewColor", "New Color"),
|
|
LOCTEXT("NewColorTooltip", "Changes the color this folder should appear as."),
|
|
FSlateIcon(),
|
|
FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecutePickColor))
|
|
);
|
|
|
|
// Clear Color (only required if any of the selection has one)
|
|
if (SelectedHasCustomColors())
|
|
{
|
|
Section.AddMenuEntry(
|
|
"ClearColor",
|
|
LOCTEXT("ClearColor", "Clear Color"),
|
|
LOCTEXT("ClearColorTooltip", "Resets the color this folder appears as."),
|
|
FSlateIcon(),
|
|
FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteResetColor))
|
|
);
|
|
}
|
|
}
|
|
|
|
// Add all the custom colors the user has chosen so far
|
|
TArray< FLinearColor > CustomColors;
|
|
if ( ContentBrowserUtils::HasCustomColors( &CustomColors ) )
|
|
{
|
|
{
|
|
FToolMenuSection& Section = Menu->AddSection("PathContextCustomColors", LOCTEXT("CustomColorsExistingColors", "Existing Colors") );
|
|
for ( int32 ColorIndex = 0; ColorIndex < CustomColors.Num(); ColorIndex++ )
|
|
{
|
|
const FLinearColor& Color = CustomColors[ ColorIndex ];
|
|
Section.AddEntry(FToolMenuEntry::InitWidget(
|
|
NAME_None,
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(2, 0, 0, 0)
|
|
[
|
|
SNew(SButton)
|
|
.ButtonStyle( FAppStyle::Get(), "Menu.Button" )
|
|
.OnClicked( this, &FPathContextMenu::OnColorClicked, Color )
|
|
[
|
|
SNew(SColorBlock)
|
|
.Color( Color )
|
|
.Size( FVector2D(77,16) )
|
|
]
|
|
],
|
|
FText::GetEmpty(),
|
|
/*bNoIndent=*/true
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPathContextMenu::ExecuteExplore()
|
|
{
|
|
ContentBrowserUtils::ExploreFolders(SelectedFolders, ParentContent.Pin().ToSharedRef());
|
|
}
|
|
|
|
bool FPathContextMenu::CanExecuteRename() const
|
|
{
|
|
return SelectedFolders.Num() == 1 && SelectedFolders[0].CanRename(nullptr);
|
|
}
|
|
|
|
void FPathContextMenu::ExecuteRename(EContentBrowserViewContext ViewContext)
|
|
{
|
|
check(SelectedFolders.Num() == 1);
|
|
if (OnRenameFolderRequested.IsBound())
|
|
{
|
|
OnRenameFolderRequested.Execute(SelectedFolders[0], ViewContext);
|
|
}
|
|
}
|
|
|
|
void FPathContextMenu::ExecuteResetColor()
|
|
{
|
|
ResetColors();
|
|
}
|
|
|
|
void FPathContextMenu::ExecutePickColor()
|
|
{
|
|
if (SelectedFolders.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Spawn a color picker, so the user can select which color they want
|
|
FLinearColor InitialColor = ContentBrowserUtils::GetDefaultColor();
|
|
if (SelectedFolders.Num() > 0)
|
|
{
|
|
// Make sure an color entry exists for all the paths, otherwise they won't update in realtime with the widget color
|
|
for (const FContentBrowserItem& SelectedItem : SelectedFolders)
|
|
{
|
|
const FString Path = SelectedItem.GetInvariantPath().ToString();
|
|
|
|
TOptional<FLinearColor> Color = ContentBrowserUtils::GetPathColor(Path);
|
|
if (Color.IsSet())
|
|
{
|
|
// Default the color to the first valid entry
|
|
InitialColor = Color.GetValue();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FColorPickerArgs PickerArgs = FColorPickerArgs(InitialColor, FOnLinearColorValueChanged::CreateSP(this, &FPathContextMenu::OnLinearColorValueChanged));
|
|
PickerArgs.bIsModal = false;
|
|
PickerArgs.ParentWidget = ParentContent.Pin();
|
|
|
|
OpenColorPicker(PickerArgs);
|
|
}
|
|
|
|
void FPathContextMenu::ExecuteFavorite()
|
|
{
|
|
TArray<FString> PathsToUpdate;
|
|
for (const FContentBrowserItem& SelectedItem : SelectedFolders)
|
|
{
|
|
PathsToUpdate.Add(SelectedItem.GetVirtualPath().ToString());
|
|
}
|
|
|
|
OnFolderFavoriteToggled.ExecuteIfBound(PathsToUpdate);
|
|
}
|
|
|
|
void FPathContextMenu::ExecutePrivateContentEdit()
|
|
{
|
|
TArray<FString> PathsToUpdate;
|
|
for (const FContentBrowserItem& SelectedItem : SelectedFolders)
|
|
{
|
|
PathsToUpdate.Add(SelectedItem.GetVirtualPath().ToString());
|
|
}
|
|
|
|
OnPrivateContentEditToggled.ExecuteIfBound(PathsToUpdate);
|
|
}
|
|
|
|
void FPathContextMenu::OnLinearColorValueChanged(const FLinearColor InColor)
|
|
{
|
|
OnColorClicked(InColor);
|
|
}
|
|
|
|
FReply FPathContextMenu::OnColorClicked( const FLinearColor InColor )
|
|
{
|
|
// Make sure a color entry exists for all the paths, otherwise it can't save correctly
|
|
for (const FContentBrowserItem& SelectedItem : SelectedFolders)
|
|
{
|
|
const FString Path = SelectedItem.GetInvariantPath().ToString();
|
|
ContentBrowserUtils::SetPathColor(Path, InColor);
|
|
}
|
|
|
|
// Dismiss the menu here, as we can't make the 'clear' option appear if a folder has just had a color set for the first time
|
|
FSlateApplication::Get().DismissAllMenus();
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
void FPathContextMenu::ResetColors()
|
|
{
|
|
// Clear the custom colors for all the selected paths
|
|
for (const FContentBrowserItem& SelectedItem : SelectedFolders)
|
|
{
|
|
ContentBrowserUtils::SetPathColor(SelectedItem.GetInvariantPath().ToString(), TOptional<FLinearColor>());
|
|
}
|
|
}
|
|
|
|
void FPathContextMenu::ExecuteSaveFolder()
|
|
{
|
|
SaveFilesWithinSelectedFolders(EContentBrowserItemSaveFlags::SaveOnlyIfDirty | EContentBrowserItemSaveFlags::SaveOnlyIfLoaded);
|
|
}
|
|
|
|
void FPathContextMenu::ExecuteResaveFolder()
|
|
{
|
|
SaveFilesWithinSelectedFolders(EContentBrowserItemSaveFlags::None);
|
|
}
|
|
|
|
void FPathContextMenu::CopySelectedFolder()
|
|
{
|
|
CopySelectedFoldersToClipoard();
|
|
}
|
|
|
|
void FPathContextMenu::SaveFilesWithinSelectedFolders(EContentBrowserItemSaveFlags InSaveFlags)
|
|
{
|
|
UContentBrowserDataSubsystem* ContentBrowserData = IContentBrowserDataModule::Get().GetSubsystem();
|
|
|
|
// Batch these by their data sources
|
|
TMap<UContentBrowserDataSource*, TArray<FContentBrowserItemData>> SourcesAndItems;
|
|
for (const FContentBrowserItem& SelectedItem : SelectedFolders)
|
|
{
|
|
FContentBrowserDataFilter SubFileFilter;
|
|
SubFileFilter.bRecursivePaths = true;
|
|
SubFileFilter.ItemTypeFilter = EContentBrowserItemTypeFilter::IncludeFiles;
|
|
|
|
// Get the file items within this folder
|
|
ContentBrowserData->EnumerateItemsUnderPath(SelectedItem.GetVirtualPath(), SubFileFilter, [InSaveFlags , &SourcesAndItems](FContentBrowserItemData&& InFileItem)
|
|
{
|
|
if (UContentBrowserDataSource* ItemDataSource = InFileItem.GetOwnerDataSource())
|
|
{
|
|
if (ItemDataSource->CanSaveItem(InFileItem, InSaveFlags, nullptr))
|
|
{
|
|
TArray<FContentBrowserItemData>& ItemsForSource = SourcesAndItems.FindOrAdd(ItemDataSource);
|
|
ItemsForSource.Add(MoveTemp(InFileItem));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
// Execute the operation now
|
|
for (const auto& SourceAndItemsPair : SourcesAndItems)
|
|
{
|
|
SourceAndItemsPair.Key->BulkSaveItems(SourceAndItemsPair.Value, InSaveFlags);
|
|
}
|
|
}
|
|
|
|
void FPathContextMenu::CopySelectedFoldersToClipoard()
|
|
{
|
|
ContentBrowserUtils::CopyFolderReferencesToClipboard(SelectedFolders);
|
|
}
|
|
|
|
bool FPathContextMenu::CanExecuteDelete() const
|
|
{
|
|
bool bCanDelete = false;
|
|
for (const FContentBrowserItem& SelectedItem : SelectedFolders)
|
|
{
|
|
bCanDelete |= SelectedItem.CanDelete();
|
|
}
|
|
return bCanDelete;
|
|
}
|
|
|
|
FText FPathContextMenu::GetDeleteToolTip() const
|
|
{
|
|
FText ErrorMessage;
|
|
bool bCanDelete = false;
|
|
for (const FContentBrowserItem& SelectedItem : SelectedFolders)
|
|
{
|
|
bCanDelete |= SelectedItem.CanDelete(&ErrorMessage);
|
|
}
|
|
|
|
if (!bCanDelete && !ErrorMessage.IsEmpty())
|
|
{
|
|
return ErrorMessage;
|
|
}
|
|
|
|
return LOCTEXT("DeleteFolderTooltip", "Removes this folder and all assets it contains.");
|
|
}
|
|
|
|
void FPathContextMenu::ExecuteDelete()
|
|
{
|
|
// If we had any folders selected, ask the user whether they want to delete them
|
|
// as it can be slow to build the deletion dialog on an accidental click
|
|
TSharedPtr<SWidget> ParentContentPtr = ParentContent.Pin();
|
|
if (ParentContentPtr && SelectedFolders.Num() > 0)
|
|
{
|
|
FText Prompt;
|
|
if (SelectedFolders.Num() == 1)
|
|
{
|
|
Prompt = FText::Format(LOCTEXT("FolderDeleteConfirm_Single", "Delete folder '{0}'?"), SelectedFolders[0].GetDisplayName());
|
|
}
|
|
else
|
|
{
|
|
Prompt = FText::Format(LOCTEXT("FolderDeleteConfirm_Multiple", "Delete {0} folders?"), SelectedFolders.Num());
|
|
}
|
|
|
|
// Spawn a confirmation dialog since this is potentially a highly destructive operation
|
|
ContentBrowserUtils::DisplayConfirmationPopup(
|
|
Prompt,
|
|
LOCTEXT("FolderDeleteConfirm_Yes", "Delete"),
|
|
LOCTEXT("FolderDeleteConfirm_No", "Cancel"),
|
|
ParentContentPtr.ToSharedRef(),
|
|
FOnClicked::CreateSP(this, &FPathContextMenu::ExecuteDeleteFolderConfirmed)
|
|
);
|
|
}
|
|
}
|
|
|
|
FReply FPathContextMenu::ExecuteDeleteFolderConfirmed()
|
|
{
|
|
// Batch these by their data sources
|
|
TMap<UContentBrowserDataSource*, TArray<FContentBrowserItemData>> SourcesAndItems;
|
|
for (const FContentBrowserItem& SelectedItem : SelectedFolders)
|
|
{
|
|
FContentBrowserItem::FItemDataArrayView ItemDataArray = SelectedItem.GetInternalItems();
|
|
for (const FContentBrowserItemData& ItemData : ItemDataArray)
|
|
{
|
|
if (UContentBrowserDataSource* ItemDataSource = ItemData.GetOwnerDataSource())
|
|
{
|
|
FText DeleteErrorMsg;
|
|
if (ItemDataSource->CanDeleteItem(ItemData, &DeleteErrorMsg))
|
|
{
|
|
TArray<FContentBrowserItemData>& ItemsForSource = SourcesAndItems.FindOrAdd(ItemDataSource);
|
|
ItemsForSource.Add(ItemData);
|
|
}
|
|
else
|
|
{
|
|
AssetViewUtils::ShowErrorNotifcation(DeleteErrorMsg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Execute the operation now
|
|
bool bDidDelete = false;
|
|
for (const auto& SourceAndItemsPair : SourcesAndItems)
|
|
{
|
|
bDidDelete |= SourceAndItemsPair.Key->BulkDeleteItems(SourceAndItemsPair.Value);
|
|
}
|
|
|
|
if (bDidDelete)
|
|
{
|
|
ResetColors();
|
|
OnFolderDeleted.ExecuteIfBound();
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
bool FPathContextMenu::SelectedHasCustomColors() const
|
|
{
|
|
for (const FContentBrowserItem& SelectedItem : SelectedFolders)
|
|
{
|
|
if (const TOptional<FLinearColor> Color = ContentBrowserUtils::GetPathColor(SelectedItem.GetInvariantPath().ToString()))
|
|
{
|
|
// Ignore any that are the default color, in case the user used the deprecated SaveColor with bForce
|
|
if (!Color->Equals(ContentBrowserUtils::GetDefaultColor()))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|