1646 lines
51 KiB
C++
1646 lines
51 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "ContentBrowserUtils.h"
|
|
|
|
#include "AssetRegistry/ARFilter.h"
|
|
#include "AssetRegistry/AssetData.h"
|
|
#include "CollectionManagerModule.h"
|
|
#include "CollectionViewUtils.h"
|
|
#include "Containers/Map.h"
|
|
#include "Containers/StringView.h"
|
|
#include "Containers/VersePath.h"
|
|
#include "ContentBrowserConfig.h"
|
|
#include "ContentBrowserDataFilter.h"
|
|
#include "ContentBrowserDataSource.h"
|
|
#include "ContentBrowserDataSubsystem.h"
|
|
#include "ContentBrowserDataUtils.h"
|
|
#include "ContentBrowserItem.h"
|
|
#include "ContentBrowserItemData.h"
|
|
#include "ContentBrowserSingleton.h"
|
|
#include "CoreGlobals.h"
|
|
#include "Framework/Application/IMenu.h"
|
|
#include "Framework/Application/MenuStack.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "FrontendFilterBase.h"
|
|
#include "HAL/FileManager.h"
|
|
#include "HAL/PlatformApplicationMisc.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "HAL/PlatformMisc.h"
|
|
#include "HAL/PlatformProcess.h"
|
|
#include "IAssetTools.h"
|
|
#include "ICollectionContainer.h"
|
|
#include "ICollectionManager.h"
|
|
#include "ICollectionSource.h"
|
|
#include "IContentBrowserDataModule.h"
|
|
#include "Input/Reply.h"
|
|
#include "Interfaces/IPluginManager.h"
|
|
#include "Layout/Children.h"
|
|
#include "Layout/Margin.h"
|
|
#include "Layout/SlateRect.h"
|
|
#include "Layout/WidgetPath.h"
|
|
#include "Math/Color.h"
|
|
#include "Math/UnrealMathSSE.h"
|
|
#include "Math/Vector2D.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Misc/Attribute.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "Misc/NamePermissionList.h"
|
|
#include "Misc/Optional.h"
|
|
#include "Misc/Paths.h"
|
|
#include "SAssetView.h"
|
|
#include "Settings/ContentBrowserSettings.h"
|
|
#include "SFilterList.h"
|
|
#include "SPathView.h"
|
|
#include "SlateOptMacros.h"
|
|
#include "SlotBase.h"
|
|
#include "SourceControlOperations.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Styling/SlateColor.h"
|
|
#include "Templates/UnrealTemplate.h"
|
|
#include "Types/SlateEnums.h"
|
|
#include "UObject/TopLevelAssetPath.h"
|
|
#include "Widgets/DeclarativeSyntaxSupport.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Layout/SBorder.h"
|
|
#include "Widgets/Layout/SUniformGridPanel.h"
|
|
#include "Widgets/SBoxPanel.h"
|
|
#include "Widgets/SCompoundWidget.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Algo/Transform.h"
|
|
#include "ContentBrowserStyle.h"
|
|
#include "SNavigationBar.h"
|
|
|
|
class SWidget;
|
|
struct FGeometry;
|
|
struct FPointerEvent;
|
|
|
|
#define LOCTEXT_NAMESPACE "ContentBrowser"
|
|
|
|
namespace ContentBrowserUtils
|
|
{
|
|
/** Converts a virtual path such as /All/Plugins -> /Plugins or /All/Game -> /Game */
|
|
FString ConvertVirtualPathToInvariantPathString(const FString& VirtualPath)
|
|
{
|
|
FName ConvertedPath;
|
|
IContentBrowserDataModule::Get().GetSubsystem()->TryConvertVirtualPath(FName(VirtualPath), ConvertedPath);
|
|
return ConvertedPath.ToString();
|
|
}
|
|
}
|
|
|
|
class SContentBrowserPopup : public SCompoundWidget
|
|
{
|
|
public:
|
|
SLATE_BEGIN_ARGS( SContentBrowserPopup ){}
|
|
|
|
SLATE_ATTRIBUTE( FText, Message )
|
|
SLATE_ARGUMENT( ContentBrowserUtils::EDisplayMessageType, MessageType )
|
|
|
|
SLATE_END_ARGS()
|
|
|
|
/** Constructs this widget with InArgs */
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
void Construct( const FArguments& InArgs )
|
|
{
|
|
Message = InArgs._Message;
|
|
MessageType = InArgs._MessageType;
|
|
|
|
ChildSlot
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FAppStyle::GetBrush("Menu.Background"))
|
|
.Padding(10.f)
|
|
.OnMouseButtonDown(this, &SContentBrowserPopup::OnBorderClicked)
|
|
.BorderBackgroundColor(this, &SContentBrowserPopup::GetBorderBackgroundColor)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0.f, 0.f, 4.f, 0.f)
|
|
[
|
|
SNew(SImage)
|
|
.Image(this, &SContentBrowserPopup::GetDisplayMessageIconBrush)
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(InArgs._Message)
|
|
.WrapTextAt(450)
|
|
]
|
|
]
|
|
];
|
|
}
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
const FSlateBrush* GetDisplayMessageIconBrush() const
|
|
{
|
|
FName BrushName = FName(TEXT(""));
|
|
switch (MessageType)
|
|
{
|
|
case ContentBrowserUtils::EDisplayMessageType::Successful:
|
|
BrushName = FName(TEXT("ContentBrowser.PopupMessageIcon.Check"));
|
|
break;
|
|
|
|
case ContentBrowserUtils::EDisplayMessageType::Info:
|
|
BrushName = FName(TEXT("ContentBrowser.PopupMessageIcon.Info"));
|
|
break;
|
|
|
|
case ContentBrowserUtils::EDisplayMessageType::Warning:
|
|
BrushName = FName(TEXT("Icons.Warning.Solid"));
|
|
break;
|
|
|
|
case ContentBrowserUtils::EDisplayMessageType::Error:
|
|
BrushName = FName(TEXT("Icons.Error.Solid"));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return UE::ContentBrowser::Private::FContentBrowserStyle::Get().GetBrush(BrushName);
|
|
}
|
|
|
|
static void DisplayMessage( const FText& Message, const FSlateRect& ScreenAnchor, TSharedRef<SWidget> ParentContent, ContentBrowserUtils::EDisplayMessageType InMessageType )
|
|
{
|
|
TSharedRef<SContentBrowserPopup> PopupContent = SNew(SContentBrowserPopup)
|
|
.Message(Message)
|
|
.MessageType(InMessageType);
|
|
|
|
const FVector2D ScreenLocation = FVector2D(ScreenAnchor.Left, ScreenAnchor.Top);
|
|
const bool bFocusImmediately = true;
|
|
const FVector2D SummonLocationSize = ScreenAnchor.GetSize();
|
|
|
|
TSharedPtr<IMenu> Menu = FSlateApplication::Get().PushMenu(
|
|
ParentContent,
|
|
FWidgetPath(),
|
|
PopupContent,
|
|
ScreenLocation,
|
|
FPopupTransitionEffect( FPopupTransitionEffect::TopMenu ),
|
|
bFocusImmediately,
|
|
SummonLocationSize);
|
|
|
|
PopupContent->SetMenu(Menu);
|
|
}
|
|
|
|
private:
|
|
void SetMenu(const TSharedPtr<IMenu>& InMenu)
|
|
{
|
|
Menu = InMenu;
|
|
}
|
|
|
|
FReply OnBorderClicked(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
if (Menu.IsValid())
|
|
{
|
|
Menu.Pin()->Dismiss();
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
FSlateColor GetBorderBackgroundColor() const
|
|
{
|
|
return IsHovered() ? FLinearColor(0.5, 0.5, 0.5, 1) : FLinearColor::White;
|
|
}
|
|
|
|
private:
|
|
ContentBrowserUtils::EDisplayMessageType MessageType = ContentBrowserUtils::EDisplayMessageType::Info;
|
|
TAttribute<FText> Message;
|
|
TWeakPtr<IMenu> Menu;
|
|
};
|
|
|
|
/** A miniture confirmation popup for quick yes/no questions */
|
|
class SContentBrowserConfirmPopup : public SCompoundWidget
|
|
{
|
|
public:
|
|
SLATE_BEGIN_ARGS( SContentBrowserConfirmPopup ) {}
|
|
|
|
/** The text to display */
|
|
SLATE_ARGUMENT(FText, Prompt)
|
|
|
|
/** The Yes Button to display */
|
|
SLATE_ARGUMENT(FText, YesText)
|
|
|
|
/** The No Button to display */
|
|
SLATE_ARGUMENT(FText, NoText)
|
|
|
|
/** Invoked when yes is clicked */
|
|
SLATE_EVENT(FOnClicked, OnYesClicked)
|
|
|
|
/** Invoked when no is clicked */
|
|
SLATE_EVENT(FOnClicked, OnNoClicked)
|
|
|
|
SLATE_END_ARGS()
|
|
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
void Construct( const FArguments& InArgs )
|
|
{
|
|
OnYesClicked = InArgs._OnYesClicked;
|
|
OnNoClicked = InArgs._OnNoClicked;
|
|
|
|
ChildSlot
|
|
[
|
|
SNew(SBorder)
|
|
. BorderImage(FAppStyle::GetBrush("Menu.Background"))
|
|
. Padding(10.f)
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.f, 0.f, 0.f, 5.f)
|
|
.HAlign(HAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(InArgs._Prompt)
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.HAlign(HAlign_Center)
|
|
[
|
|
SNew(SUniformGridPanel)
|
|
.SlotPadding(3.f)
|
|
+ SUniformGridPanel::Slot(0, 0)
|
|
.HAlign(HAlign_Fill)
|
|
[
|
|
SNew(SButton)
|
|
.HAlign(HAlign_Center)
|
|
.Text(InArgs._YesText)
|
|
.OnClicked( this, &SContentBrowserConfirmPopup::YesClicked )
|
|
]
|
|
|
|
+ SUniformGridPanel::Slot(1, 0)
|
|
.HAlign(HAlign_Fill)
|
|
[
|
|
SNew(SButton)
|
|
.HAlign(HAlign_Center)
|
|
.Text(InArgs._NoText)
|
|
.OnClicked( this, &SContentBrowserConfirmPopup::NoClicked )
|
|
]
|
|
]
|
|
]
|
|
];
|
|
}
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
/** Opens the popup using the specified component as its parent */
|
|
void OpenPopup(const TSharedRef<SWidget>& ParentContent)
|
|
{
|
|
// Show dialog to confirm the delete
|
|
Menu = FSlateApplication::Get().PushMenu(
|
|
ParentContent,
|
|
FWidgetPath(),
|
|
SharedThis(this),
|
|
FSlateApplication::Get().GetCursorPos(),
|
|
FPopupTransitionEffect( FPopupTransitionEffect::TopMenu )
|
|
);
|
|
}
|
|
|
|
private:
|
|
/** The yes button was clicked */
|
|
FReply YesClicked()
|
|
{
|
|
if ( OnYesClicked.IsBound() )
|
|
{
|
|
OnYesClicked.Execute();
|
|
}
|
|
|
|
if (Menu.IsValid())
|
|
{
|
|
Menu.Pin()->Dismiss();
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
/** The no button was clicked */
|
|
FReply NoClicked()
|
|
{
|
|
if ( OnNoClicked.IsBound() )
|
|
{
|
|
OnNoClicked.Execute();
|
|
}
|
|
|
|
if (Menu.IsValid())
|
|
{
|
|
Menu.Pin()->Dismiss();
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
/** The IMenu prepresenting this popup */
|
|
TWeakPtr<IMenu> Menu;
|
|
|
|
/** Delegates for button clicks */
|
|
FOnClicked OnYesClicked;
|
|
FOnClicked OnNoClicked;
|
|
};
|
|
|
|
|
|
void ContentBrowserUtils::DisplayMessage(const FText& Message, const FSlateRect& ScreenAnchor, const TSharedRef<SWidget>& ParentContent, EDisplayMessageType InMessageType)
|
|
{
|
|
SContentBrowserPopup::DisplayMessage(Message, ScreenAnchor, ParentContent, InMessageType);
|
|
}
|
|
|
|
void ContentBrowserUtils::DisplayConfirmationPopup(const FText& Message, const FText& YesString, const FText& NoString, const TSharedRef<SWidget>& ParentContent, const FOnClicked& OnYesClicked, const FOnClicked& OnNoClicked)
|
|
{
|
|
TSharedRef<SContentBrowserConfirmPopup> Popup =
|
|
SNew(SContentBrowserConfirmPopup)
|
|
.Prompt(Message)
|
|
.YesText(YesString)
|
|
.NoText(NoString)
|
|
.OnYesClicked( OnYesClicked )
|
|
.OnNoClicked( OnNoClicked );
|
|
|
|
Popup->OpenPopup(ParentContent);
|
|
}
|
|
|
|
FString ContentBrowserUtils::GetItemReferencesText(const TArray<FContentBrowserItem>& Items)
|
|
{
|
|
TArray<FContentBrowserItem> SortedItems = Items;
|
|
SortedItems.Sort([](const FContentBrowserItem& One, const FContentBrowserItem& Two)
|
|
{
|
|
return One.GetVirtualPath().Compare(Two.GetVirtualPath()) < 0;
|
|
});
|
|
|
|
FString Result;
|
|
for (const FContentBrowserItem& Item : SortedItems)
|
|
{
|
|
if (ensure(!Item.IsFolder()))
|
|
{
|
|
Item.AppendItemReference(Result);
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
FString ContentBrowserUtils::GetItemObjectPathText(const TArray<FContentBrowserItem>& Items)
|
|
{
|
|
TArray<FContentBrowserItem> SortedItems = Items;
|
|
SortedItems.Sort([](const FContentBrowserItem& One, const FContentBrowserItem& Two)
|
|
{
|
|
return One.GetVirtualPath().Compare(Two.GetVirtualPath()) < 0;
|
|
});
|
|
|
|
FString Result;
|
|
for (const FContentBrowserItem& Item : SortedItems)
|
|
{
|
|
if (ensure(!Item.IsFolder()))
|
|
{
|
|
Item.AppendItemObjectPath(Result);
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
FString ContentBrowserUtils::GetItemPackageNameText(const TArray<FContentBrowserItem>& Items)
|
|
{
|
|
TArray<FContentBrowserItem> SortedItems = Items;
|
|
SortedItems.Sort([](const FContentBrowserItem& One, const FContentBrowserItem& Two)
|
|
{
|
|
return One.GetVirtualPath().Compare(Two.GetVirtualPath()) < 0;
|
|
});
|
|
|
|
FString Result;
|
|
for (const FContentBrowserItem& Item : SortedItems)
|
|
{
|
|
if (ensure(!Item.IsFolder()))
|
|
{
|
|
Item.AppendItemPackageName(Result);
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
FString ContentBrowserUtils::GetFolderReferencesText(const TArray<FContentBrowserItem>& Folders)
|
|
{
|
|
TArray<FContentBrowserItem> SortedItems = Folders;
|
|
SortedItems.Sort([](const FContentBrowserItem& One, const FContentBrowserItem& Two)
|
|
{
|
|
return One.GetVirtualPath().Compare(Two.GetVirtualPath()) < 0;
|
|
});
|
|
|
|
TStringBuilder<2048> Result;
|
|
for (const FContentBrowserItem& Item : SortedItems)
|
|
{
|
|
if (ensure(Item.IsFolder()))
|
|
{
|
|
FName InternalPath = Item.GetInternalPath();
|
|
if (!InternalPath.IsNone())
|
|
{
|
|
Result << InternalPath << LINE_TERMINATOR;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result.ToString();
|
|
}
|
|
|
|
void ContentBrowserUtils::CopyItemReferencesToClipboard(const TArray<FContentBrowserItem>& ItemsToCopy)
|
|
{
|
|
FString Text = GetItemReferencesText(ItemsToCopy);
|
|
if (!Text.IsEmpty())
|
|
{
|
|
FPlatformApplicationMisc::ClipboardCopy(*Text);
|
|
}
|
|
}
|
|
|
|
void ContentBrowserUtils::CopyItemObjectPathToClipboard(const TArray<FContentBrowserItem>& ItemsToCopy)
|
|
{
|
|
FString Text = GetItemObjectPathText(ItemsToCopy);
|
|
if (!Text.IsEmpty())
|
|
{
|
|
FPlatformApplicationMisc::ClipboardCopy(*Text);
|
|
}
|
|
}
|
|
|
|
void ContentBrowserUtils::CopyItemPackageNameToClipboard(const TArray<FContentBrowserItem>& ItemsToCopy)
|
|
{
|
|
FString Text = GetItemPackageNameText(ItemsToCopy);
|
|
if (!Text.IsEmpty())
|
|
{
|
|
FPlatformApplicationMisc::ClipboardCopy(*Text);
|
|
}
|
|
}
|
|
|
|
void ContentBrowserUtils::CopyFolderReferencesToClipboard(const TArray<FContentBrowserItem>& FoldersToCopy)
|
|
{
|
|
FString Text = GetFolderReferencesText(FoldersToCopy);
|
|
if (!Text.IsEmpty())
|
|
{
|
|
FPlatformApplicationMisc::ClipboardCopy(*Text);
|
|
}
|
|
}
|
|
|
|
void ContentBrowserUtils::CopyFilePathsToClipboard(const TArray<FContentBrowserItem>& ItemsToCopy)
|
|
{
|
|
TArray<FContentBrowserItem> SortedItems = ItemsToCopy;
|
|
SortedItems.Sort([](const FContentBrowserItem& One, const FContentBrowserItem& Two)
|
|
{
|
|
return One.GetVirtualPath().Compare(Two.GetVirtualPath()) < 0;
|
|
});
|
|
|
|
FString ClipboardText;
|
|
for (const FContentBrowserItem& Item : SortedItems)
|
|
{
|
|
if (ClipboardText.Len() > 0)
|
|
{
|
|
ClipboardText += LINE_TERMINATOR;
|
|
}
|
|
|
|
FString ItemFilename;
|
|
if (Item.GetItemPhysicalPath(ItemFilename) && FPaths::FileExists(ItemFilename))
|
|
{
|
|
ItemFilename = FPaths::ConvertRelativePathToFull(ItemFilename);
|
|
FPaths::MakePlatformFilename(ItemFilename);
|
|
ClipboardText += ItemFilename;
|
|
}
|
|
else
|
|
{
|
|
// Add a message for when a user tries to copy the path to a file that doesn't exist on disk of the form
|
|
// <ItemName>: No file on disk
|
|
ClipboardText += FString::Printf(TEXT("%s: No file on disk"), *Item.GetDisplayName().ToString());
|
|
}
|
|
}
|
|
|
|
FPlatformApplicationMisc::ClipboardCopy(*ClipboardText);
|
|
}
|
|
|
|
bool ContentBrowserUtils::IsItemDeveloperContent(const FContentBrowserItem& InItem)
|
|
{
|
|
const FContentBrowserItemDataAttributeValue IsDeveloperAttributeValue = InItem.GetItemAttribute(ContentBrowserItemAttributes::ItemIsDeveloperContent);
|
|
return IsDeveloperAttributeValue.IsValid() && IsDeveloperAttributeValue.GetValue<bool>();
|
|
}
|
|
|
|
bool ContentBrowserUtils::IsItemLocalizedContent(const FContentBrowserItem& InItem)
|
|
{
|
|
const FContentBrowserItemDataAttributeValue IsLocalizedAttributeValue = InItem.GetItemAttribute(ContentBrowserItemAttributes::ItemIsLocalizedContent);
|
|
return IsLocalizedAttributeValue.IsValid() && IsLocalizedAttributeValue.GetValue<bool>();
|
|
}
|
|
|
|
bool ContentBrowserUtils::IsItemEngineContent(const FContentBrowserItem& InItem)
|
|
{
|
|
const FContentBrowserItemDataAttributeValue IsEngineAttributeValue = InItem.GetItemAttribute(ContentBrowserItemAttributes::ItemIsEngineContent);
|
|
return IsEngineAttributeValue.IsValid() && IsEngineAttributeValue.GetValue<bool>();
|
|
}
|
|
|
|
bool ContentBrowserUtils::IsItemProjectContent(const FContentBrowserItem& InItem)
|
|
{
|
|
const FContentBrowserItemDataAttributeValue IsProjectAttributeValue = InItem.GetItemAttribute(ContentBrowserItemAttributes::ItemIsProjectContent);
|
|
return IsProjectAttributeValue.IsValid() && IsProjectAttributeValue.GetValue<bool>();
|
|
}
|
|
|
|
bool ContentBrowserUtils::IsItemPluginContent(const FContentBrowserItem& InItem)
|
|
{
|
|
const FContentBrowserItemDataAttributeValue IsPluginAttributeValue = InItem.GetItemAttribute(ContentBrowserItemAttributes::ItemIsPluginContent);
|
|
return IsPluginAttributeValue.IsValid() && IsPluginAttributeValue.GetValue<bool>();
|
|
}
|
|
|
|
bool ContentBrowserUtils::IsItemPluginRootFolder(const FContentBrowserItem& InItem)
|
|
{
|
|
if (!InItem.IsFolder())
|
|
{
|
|
return false;
|
|
}
|
|
FName InternalPath = InItem.GetInternalPath();
|
|
if (InternalPath.IsNone())
|
|
{
|
|
return false;
|
|
}
|
|
FNameBuilder PathBuffer(InternalPath);
|
|
FStringView Path = PathBuffer.ToView();
|
|
if (int32 Index = 0; Path.RightChop(1).FindChar('/', Index) && Index != INDEX_NONE)
|
|
{
|
|
return false; // Contains a second slash, is not a root
|
|
}
|
|
return IsItemPluginContent(InItem);
|
|
}
|
|
|
|
bool ContentBrowserUtils::TryGetFolderBrushAndShadowName(const FContentBrowserItem& InFolder, FName& OutBrushName, FName& OutShadowBrushName)
|
|
{
|
|
if (!InFolder.IsValid() || !InFolder.IsFolder())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
OutShadowBrushName = TEXT("ContentBrowser.FolderItem.DropShadow");
|
|
const bool bDeveloperFolder = IsItemDeveloperContent(InFolder);
|
|
const bool bCodeFolder = EnumHasAnyFlags(InFolder.GetItemCategory(), EContentBrowserItemFlags::Category_Class);
|
|
const FContentBrowserItemDataAttributeValue VirtualAttributeValue = InFolder.GetItemAttribute(ContentBrowserItemAttributes::ItemIsCustomVirtualFolder);
|
|
const bool bVirtualFolder = VirtualAttributeValue.IsValid() && VirtualAttributeValue.GetValue<bool>();
|
|
const bool bPluginFolder = IsItemPluginRootFolder(InFolder);
|
|
|
|
if (bDeveloperFolder)
|
|
{
|
|
OutBrushName = TEXT("ContentBrowser.ListViewDeveloperFolderIcon");
|
|
}
|
|
else if (bCodeFolder)
|
|
{
|
|
OutBrushName = TEXT("ContentBrowser.ListViewCodeFolderIcon");
|
|
}
|
|
else if (bVirtualFolder && ShouldShowCustomVirtualFolderIcon())
|
|
{
|
|
OutBrushName = TEXT("ContentBrowser.ListViewVirtualFolderIcon");
|
|
OutShadowBrushName = TEXT("ContentBrowser.ListViewVirtualFolderShadow");
|
|
}
|
|
else if (bPluginFolder && ShouldShowPluginFolderIcon())
|
|
{
|
|
OutBrushName = TEXT("ContentBrowser.ListViewPluginFolderIcon");
|
|
}
|
|
else
|
|
{
|
|
OutBrushName = TEXT("ContentBrowser.ListViewFolderIcon");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ContentBrowserUtils::TryGetFolderBrushAndShadowNameSmall(const FContentBrowserItem& InFolder, FName& OutBrushName, FName& OutShadowBrushName)
|
|
{
|
|
if (!InFolder.IsValid() || !InFolder.IsFolder())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
OutShadowBrushName = TEXT("ContentBrowser.FolderItem.DropShadow");
|
|
const bool bDeveloperFolder = IsItemDeveloperContent(InFolder);
|
|
const bool bCodeFolder = EnumHasAnyFlags(InFolder.GetItemCategory(), EContentBrowserItemFlags::Category_Class);
|
|
const FContentBrowserItemDataAttributeValue VirtualAttributeValue = InFolder.GetItemAttribute(ContentBrowserItemAttributes::ItemIsCustomVirtualFolder);
|
|
const bool bVirtualFolder = VirtualAttributeValue.IsValid() && VirtualAttributeValue.GetValue<bool>();
|
|
const bool bPluginFolder = IsItemPluginRootFolder(InFolder);
|
|
|
|
if (bDeveloperFolder)
|
|
{
|
|
OutBrushName = TEXT("ContentBrowser.AssetTreeFolderClosedDeveloper");
|
|
}
|
|
else if (bCodeFolder)
|
|
{
|
|
OutBrushName = TEXT("ContentBrowser.AssetTreeFolderClosedCode");
|
|
}
|
|
else if (bVirtualFolder && ShouldShowCustomVirtualFolderIcon())
|
|
{
|
|
OutBrushName = TEXT("ContentBrowser.AssetTreeFolderClosedVirtual");
|
|
OutShadowBrushName = TEXT("ContentBrowser.ListViewVirtualFolderShadow");
|
|
}
|
|
else if (bPluginFolder && ShouldShowPluginFolderIcon())
|
|
{
|
|
OutBrushName = TEXT("ContentBrowser.AssetTreeFolderClosedPluginRoot");
|
|
}
|
|
else
|
|
{
|
|
OutBrushName = TEXT("ContentBrowser.AssetTreeFolderClosed");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ContentBrowserUtils::IsCollectionPath(const FString& InPath, TSharedPtr<ICollectionContainer>* OutCollectionContainer, FName* OutCollectionName, ECollectionShareType::Type* OutCollectionShareType)
|
|
{
|
|
static const FString CollectionsRootPrefix = TEXT("/Collections/");
|
|
if (InPath.StartsWith(CollectionsRootPrefix))
|
|
{
|
|
TArray<FString> PathParts;
|
|
InPath.ParseIntoArray(PathParts, TEXT("/"));
|
|
check(PathParts.Num() > 3);
|
|
|
|
// The second part of the path is the collection container id
|
|
if (OutCollectionContainer)
|
|
{
|
|
*OutCollectionContainer = FCollectionManagerModule::GetModule().Get().FindCollectionContainer(FName(PathParts[1], FNAME_Find));
|
|
}
|
|
|
|
// The third part of the path is the share type name
|
|
if (OutCollectionShareType)
|
|
{
|
|
*OutCollectionShareType = ECollectionShareType::FromString(*PathParts[2]);
|
|
}
|
|
|
|
// The fourth part of the path is the collection name
|
|
if (OutCollectionName)
|
|
{
|
|
*OutCollectionName = FName(*PathParts[3]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ContentBrowserUtils::CountPathTypes(const TArray<FString>& InPaths, int32& OutNumAssetPaths, int32& OutNumClassPaths)
|
|
{
|
|
static const FString ClassesRootPrefix = TEXT("/Classes_");
|
|
|
|
OutNumAssetPaths = 0;
|
|
OutNumClassPaths = 0;
|
|
|
|
for(const FString& Path : InPaths)
|
|
{
|
|
if(Path.StartsWith(ClassesRootPrefix))
|
|
{
|
|
++OutNumClassPaths;
|
|
}
|
|
else
|
|
{
|
|
++OutNumAssetPaths;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContentBrowserUtils::CountPathTypes(const TArray<FName>& InPaths, int32& OutNumAssetPaths, int32& OutNumClassPaths)
|
|
{
|
|
static const FString ClassesRootPrefix = TEXT("/Classes_");
|
|
|
|
OutNumAssetPaths = 0;
|
|
OutNumClassPaths = 0;
|
|
|
|
for(const FName& Path : InPaths)
|
|
{
|
|
if(Path.ToString().StartsWith(ClassesRootPrefix))
|
|
{
|
|
++OutNumClassPaths;
|
|
}
|
|
else
|
|
{
|
|
++OutNumAssetPaths;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContentBrowserUtils::CountItemTypes(const TArray<FAssetData>& InItems, int32& OutNumAssetItems, int32& OutNumClassItems)
|
|
{
|
|
OutNumAssetItems = 0;
|
|
OutNumClassItems = 0;
|
|
|
|
const FTopLevelAssetPath ClassPath(TEXT("/Script/CoreUObject"), TEXT("Class"));
|
|
for(const FAssetData& Item : InItems)
|
|
{
|
|
if(Item.AssetClassPath == ClassPath)
|
|
{
|
|
++OutNumClassItems;
|
|
}
|
|
else
|
|
{
|
|
++OutNumAssetItems;
|
|
}
|
|
}
|
|
}
|
|
|
|
FText ContentBrowserUtils::GetExploreFolderText()
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("FileManagerName"), FPlatformMisc::GetFileManagerName());
|
|
return FText::Format(NSLOCTEXT("GenericPlatform", "ShowInFileManager", "Show in {FileManagerName}"), Args);
|
|
}
|
|
|
|
void ContentBrowserUtils::ExploreFolders(const TArray<FContentBrowserItem>& InItems, const TSharedRef<SWidget>& InParentContent)
|
|
{
|
|
TArray<FString> ExploreItems;
|
|
|
|
for (const FContentBrowserItem& SelectedItem : InItems)
|
|
{
|
|
FString ItemFilename;
|
|
if (SelectedItem.GetItemPhysicalPath(ItemFilename))
|
|
{
|
|
const bool bExists = SelectedItem.IsFile() ? FPaths::FileExists(ItemFilename) : FPaths::DirectoryExists(ItemFilename);
|
|
if (bExists)
|
|
{
|
|
ExploreItems.Add(IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*ItemFilename));
|
|
}
|
|
}
|
|
}
|
|
|
|
const int32 BatchSize = 10;
|
|
const FText FileManagerName = FPlatformMisc::GetFileManagerName();
|
|
const bool bHasMultipleBatches = ExploreItems.Num() > BatchSize;
|
|
for (int32 i = 0; i < ExploreItems.Num(); ++i)
|
|
{
|
|
bool bIsBatchBoundary = (i % BatchSize) == 0;
|
|
if (bHasMultipleBatches && bIsBatchBoundary)
|
|
{
|
|
int32 RemainingCount = ExploreItems.Num() - i;
|
|
int32 NextCount = FMath::Min(BatchSize, RemainingCount);
|
|
FText Prompt = FText::Format(LOCTEXT("ExecuteExploreConfirm", "Show {0} {0}|plural(one=item,other=items) in {1}?\nThere {2}|plural(one=is,other=are) {2} remaining."), NextCount, FileManagerName, RemainingCount);
|
|
if (FMessageDialog::Open(EAppMsgType::YesNo, Prompt) != EAppReturnType::Yes)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
FPlatformProcess::ExploreFolder(*ExploreItems[i]);
|
|
}
|
|
}
|
|
|
|
bool ContentBrowserUtils::CanExploreFolders(const TArray<FContentBrowserItem>& InItems)
|
|
{
|
|
for (const FContentBrowserItem& SelectedItem : InItems)
|
|
{
|
|
FString ItemFilename;
|
|
if (SelectedItem.GetItemPhysicalPath(ItemFilename))
|
|
{
|
|
const bool bExists = SelectedItem.IsFile() ? FPaths::FileExists(ItemFilename) : FPaths::DirectoryExists(ItemFilename);
|
|
if (bExists)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename OutputContainerType>
|
|
void ConvertLegacySelectionToVirtualPathsImpl(TArrayView<const FAssetData> InAssets, TArrayView<const FString> InFolders, const bool InUseFolderPaths, OutputContainerType& OutVirtualPaths)
|
|
{
|
|
OutVirtualPaths.Reset();
|
|
if (InAssets.Num() == 0 && InFolders.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UContentBrowserDataSubsystem* ContentBrowserData = IContentBrowserDataModule::Get().GetSubsystem();
|
|
|
|
auto AppendVirtualPath = [&OutVirtualPaths](FName InPath)
|
|
{
|
|
OutVirtualPaths.Add(InPath);
|
|
return true;
|
|
};
|
|
|
|
for (const FAssetData& Asset : InAssets)
|
|
{
|
|
ContentBrowserData->Legacy_TryConvertAssetDataToVirtualPaths(Asset, InUseFolderPaths, AppendVirtualPath);
|
|
}
|
|
|
|
for (const FString& Folder : InFolders)
|
|
{
|
|
ContentBrowserData->Legacy_TryConvertPackagePathToVirtualPaths(*Folder, AppendVirtualPath);
|
|
}
|
|
}
|
|
|
|
void ContentBrowserUtils::ConvertLegacySelectionToVirtualPaths(TArrayView<const FAssetData> InAssets, TArrayView<const FString> InFolders, const bool InUseFolderPaths, TArray<FName>& OutVirtualPaths)
|
|
{
|
|
ConvertLegacySelectionToVirtualPathsImpl(InAssets, InFolders, InUseFolderPaths, OutVirtualPaths);
|
|
}
|
|
|
|
void ContentBrowserUtils::ConvertLegacySelectionToVirtualPaths(TArrayView<const FAssetData> InAssets, TArrayView<const FString> InFolders, const bool InUseFolderPaths, TSet<FName>& OutVirtualPaths)
|
|
{
|
|
ConvertLegacySelectionToVirtualPathsImpl(InAssets, InFolders, InUseFolderPaths, OutVirtualPaths);
|
|
}
|
|
|
|
void ContentBrowserUtils::AppendAssetFilterToContentBrowserFilter(const FARFilter& InAssetFilter, const TSharedPtr<FPathPermissionList>& InAssetClassPermissionList, const TSharedPtr<FPathPermissionList>& InFolderPermissionList, FContentBrowserDataFilter& OutDataFilter)
|
|
{
|
|
if (InAssetFilter.SoftObjectPaths.Num() > 0 || InAssetFilter.TagsAndValues.Num() > 0 || InAssetFilter.bIncludeOnlyOnDiskAssets)
|
|
{
|
|
FContentBrowserDataObjectFilter& ObjectFilter = OutDataFilter.ExtraFilters.FindOrAddFilter<FContentBrowserDataObjectFilter>();
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
|
// TODO: Modify this API to also use FSoftObjectPath with deprecation
|
|
ObjectFilter.ObjectNamesToInclude = UE::SoftObjectPath::Private::ConvertSoftObjectPaths(InAssetFilter.SoftObjectPaths);
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
|
ObjectFilter.TagsAndValuesToInclude = InAssetFilter.TagsAndValues;
|
|
ObjectFilter.bOnDiskObjectsOnly = InAssetFilter.bIncludeOnlyOnDiskAssets;
|
|
}
|
|
|
|
if (InAssetFilter.PackageNames.Num() > 0 || InAssetFilter.PackagePaths.Num() > 0 || (InFolderPermissionList && InFolderPermissionList->HasFiltering()))
|
|
{
|
|
FContentBrowserDataPackageFilter& PackageFilter = OutDataFilter.ExtraFilters.FindOrAddFilter<FContentBrowserDataPackageFilter>();
|
|
PackageFilter.PackageNamesToInclude = InAssetFilter.PackageNames;
|
|
PackageFilter.PackagePathsToInclude = InAssetFilter.PackagePaths;
|
|
PackageFilter.bRecursivePackagePathsToInclude = InAssetFilter.bRecursivePaths;
|
|
PackageFilter.PathPermissionList = InFolderPermissionList;
|
|
}
|
|
|
|
if (InAssetFilter.ClassPaths.Num() > 0 || (InAssetClassPermissionList && InAssetClassPermissionList->HasFiltering()))
|
|
{
|
|
FContentBrowserDataClassFilter& ClassFilter = OutDataFilter.ExtraFilters.FindOrAddFilter<FContentBrowserDataClassFilter>();
|
|
for (FTopLevelAssetPath ClassPathName : InAssetFilter.ClassPaths)
|
|
{
|
|
ClassFilter.ClassNamesToInclude.Add(ClassPathName.ToString());
|
|
}
|
|
ClassFilter.bRecursiveClassNamesToInclude = InAssetFilter.bRecursiveClasses;
|
|
if (InAssetFilter.bRecursiveClasses)
|
|
{
|
|
for (FTopLevelAssetPath ClassPathName : InAssetFilter.RecursiveClassPathsExclusionSet)
|
|
{
|
|
ClassFilter.ClassNamesToExclude.Add(ClassPathName.ToString());
|
|
}
|
|
ClassFilter.bRecursiveClassNamesToExclude = false;
|
|
}
|
|
ClassFilter.ClassPermissionList = InAssetClassPermissionList;
|
|
}
|
|
}
|
|
|
|
TSharedPtr<FPathPermissionList> ContentBrowserUtils::GetCombinedFolderPermissionList(const TSharedPtr<FPathPermissionList>& FolderPermissionList, const TSharedPtr<FPathPermissionList>& WritableFolderPermissionList)
|
|
{
|
|
TSharedPtr<FPathPermissionList> CombinedFolderPermissionList;
|
|
|
|
const bool bHidingFolders = FolderPermissionList && FolderPermissionList->HasFiltering();
|
|
const bool bHidingReadOnlyFolders = WritableFolderPermissionList && WritableFolderPermissionList->HasFiltering();
|
|
if (bHidingFolders || bHidingReadOnlyFolders)
|
|
{
|
|
CombinedFolderPermissionList = MakeShared<FPathPermissionList>();
|
|
|
|
if (bHidingReadOnlyFolders && bHidingFolders)
|
|
{
|
|
FPathPermissionList IntersectedFilter = FolderPermissionList->CombinePathFilters(*WritableFolderPermissionList.Get());
|
|
CombinedFolderPermissionList->Append(IntersectedFilter);
|
|
}
|
|
else if (bHidingReadOnlyFolders)
|
|
{
|
|
CombinedFolderPermissionList->Append(*WritableFolderPermissionList);
|
|
}
|
|
else if (bHidingFolders)
|
|
{
|
|
CombinedFolderPermissionList->Append(*FolderPermissionList);
|
|
}
|
|
}
|
|
|
|
return CombinedFolderPermissionList;
|
|
}
|
|
|
|
bool ContentBrowserUtils::CanDeleteFromAssetView(TWeakPtr<SAssetView> AssetView, FText* OutErrorMsg)
|
|
{
|
|
if (TSharedPtr<SAssetView> AssetViewPin = AssetView.Pin())
|
|
{
|
|
const TArray<FContentBrowserItem> SelectedItems = AssetViewPin->GetSelectedItems();
|
|
|
|
bool bCanDelete = false;
|
|
for (const FContentBrowserItem& SelectedItem : SelectedItems)
|
|
{
|
|
bCanDelete |= SelectedItem.CanDelete(OutErrorMsg);
|
|
}
|
|
return bCanDelete;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ContentBrowserUtils::CanRenameFromAssetView(TWeakPtr<SAssetView> AssetView, FText* OutErrorMsg)
|
|
{
|
|
if (TSharedPtr<SAssetView> AssetViewPin = AssetView.Pin())
|
|
{
|
|
const TArray<FContentBrowserItem> SelectedItems = AssetViewPin->GetSelectedItems();
|
|
return SelectedItems.Num() == 1 && SelectedItems[0].CanRename(nullptr, OutErrorMsg) && !AssetViewPin->IsThumbnailEditMode();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ContentBrowserUtils::CanDeleteFromPathView(TWeakPtr<SPathView> PathView, FText* OutErrorMsg)
|
|
{
|
|
if (TSharedPtr<SPathView> PathViewPin = PathView.Pin())
|
|
{
|
|
const TArray<FContentBrowserItem> SelectedItems = PathViewPin->GetSelectedFolderItems();
|
|
|
|
bool bCanDelete = false;
|
|
for (const FContentBrowserItem& SelectedItem : SelectedItems)
|
|
{
|
|
bCanDelete |= SelectedItem.CanDelete(OutErrorMsg);
|
|
}
|
|
return bCanDelete;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ContentBrowserUtils::CanRenameFromPathView(TWeakPtr<SPathView> PathView, FText* OutErrorMsg)
|
|
{
|
|
if (TSharedPtr<SPathView> PathViewPin = PathView.Pin())
|
|
{
|
|
const TArray<FContentBrowserItem> SelectedItems = PathViewPin->GetSelectedFolderItems();
|
|
return SelectedItems.Num() == 1 && SelectedItems[0].CanRename(nullptr, OutErrorMsg);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FName ContentBrowserUtils::GetInvariantPath(const FContentBrowserItemPath& ItemPath)
|
|
{
|
|
if (!ItemPath.HasInternalPath())
|
|
{
|
|
FName InvariantPath;
|
|
const EContentBrowserPathType AssetPathType = IContentBrowserDataModule::Get().GetSubsystem()->TryConvertVirtualPath(ItemPath.GetVirtualPathName(), InvariantPath);
|
|
if (AssetPathType == EContentBrowserPathType::Virtual)
|
|
{
|
|
return InvariantPath;
|
|
}
|
|
else
|
|
{
|
|
return NAME_None;
|
|
}
|
|
}
|
|
|
|
return ItemPath.GetInternalPathName();
|
|
}
|
|
|
|
EContentBrowserIsFolderVisibleFlags ContentBrowserUtils::GetIsFolderVisibleFlags(const bool bDisplayEmpty)
|
|
{
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
|
return EContentBrowserIsFolderVisibleFlags::Default | (bDisplayEmpty ? EContentBrowserIsFolderVisibleFlags::None : EContentBrowserIsFolderVisibleFlags::HideEmptyFolders);
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
|
}
|
|
|
|
bool ContentBrowserUtils::IsFavoriteFolder(const FString& FolderPath)
|
|
{
|
|
return IsFavoriteFolder(FContentBrowserItemPath(FolderPath, EContentBrowserPathType::Virtual));
|
|
}
|
|
|
|
bool ContentBrowserUtils::IsFavoriteFolder(const FContentBrowserItemPath& FolderPath)
|
|
{
|
|
const FName InvariantPath = ContentBrowserUtils::GetInvariantPath(FolderPath);
|
|
if (!InvariantPath.IsNone())
|
|
{
|
|
return FContentBrowserSingleton::Get().FavoriteFolderPaths.Contains(InvariantPath.ToString());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ContentBrowserUtils::AddFavoriteFolder(const FString& FolderPath, bool bFlushConfig /*= true*/)
|
|
{
|
|
AddFavoriteFolder(FContentBrowserItemPath(FolderPath, EContentBrowserPathType::Virtual));
|
|
}
|
|
|
|
void ContentBrowserUtils::AddFavoriteFolder(const FContentBrowserItemPath& FolderPath)
|
|
{
|
|
const FName InvariantPath = ContentBrowserUtils::GetInvariantPath(FolderPath);
|
|
if (InvariantPath.IsNone())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FString InvariantFolder = InvariantPath.ToString();
|
|
|
|
FContentBrowserSingleton::Get().FavoriteFolderPaths.AddUnique(InvariantFolder);
|
|
|
|
if (UContentBrowserConfig* EditorConfig = UContentBrowserConfig::Get())
|
|
{
|
|
EditorConfig->Favorites.Add(InvariantFolder);
|
|
|
|
UContentBrowserConfig::Get()->SaveEditorConfig();
|
|
}
|
|
|
|
FContentBrowserSingleton::Get().BroadcastFavoritesChanged(FolderPath, true);
|
|
}
|
|
|
|
void ContentBrowserUtils::RemoveFavoriteFolder(const FContentBrowserItemPath& FolderPath)
|
|
{
|
|
const FName InvariantPath = ContentBrowserUtils::GetInvariantPath(FolderPath);
|
|
if (InvariantPath.IsNone())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FString InvariantFolder = InvariantPath.ToString();
|
|
|
|
FContentBrowserSingleton::Get().FavoriteFolderPaths.Remove(InvariantFolder);
|
|
|
|
if (UContentBrowserConfig* EditorConfig = UContentBrowserConfig::Get())
|
|
{
|
|
EditorConfig->Favorites.Remove(InvariantFolder);
|
|
|
|
UContentBrowserConfig::Get()->SaveEditorConfig();
|
|
}
|
|
|
|
FContentBrowserSingleton::Get().BroadcastFavoritesChanged(FolderPath, false);
|
|
}
|
|
|
|
void ContentBrowserUtils::RemoveFavoriteFolder(const FString& FolderPath, bool bFlushConfig)
|
|
{
|
|
RemoveFavoriteFolder(FContentBrowserItemPath(FolderPath, EContentBrowserPathType::Virtual));
|
|
}
|
|
|
|
const TArray<FString>& ContentBrowserUtils::GetFavoriteFolders()
|
|
{
|
|
return FContentBrowserSingleton::Get().FavoriteFolderPaths;
|
|
}
|
|
|
|
void ContentBrowserUtils::AddShowPrivateContentFolder(const FStringView VirtualFolderPath, const FName Owner)
|
|
{
|
|
FContentBrowserSingleton& ContentBrowserSingleton = FContentBrowserSingleton::Get();
|
|
|
|
if (!ContentBrowserSingleton.IsFolderShowPrivateContentToggleable(VirtualFolderPath))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FName InvariantPath;
|
|
IContentBrowserDataModule::Get().GetSubsystem()->TryConvertVirtualPath(VirtualFolderPath, InvariantPath);
|
|
|
|
const TSharedPtr<FPathPermissionList>& ShowPrivateContentPermissionList = ContentBrowserSingleton.GetShowPrivateContentPermissionList();
|
|
|
|
ShowPrivateContentPermissionList->AddAllowListItem(Owner, InvariantPath);
|
|
|
|
ContentBrowserSingleton.SetPrivateContentPermissionListDirty();
|
|
}
|
|
|
|
void ContentBrowserUtils::RemoveShowPrivateContentFolder(const FStringView VirtualFolderPath, const FName Owner)
|
|
{
|
|
FContentBrowserSingleton& ContentBrowserSingleton = FContentBrowserSingleton::Get();
|
|
|
|
if (!ContentBrowserSingleton.IsFolderShowPrivateContentToggleable(VirtualFolderPath))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FName InvariantPath;
|
|
IContentBrowserDataModule::Get().GetSubsystem()->TryConvertVirtualPath(VirtualFolderPath, InvariantPath);
|
|
|
|
const TSharedPtr<FPathPermissionList>& ShowPrivateContentPermissionList = ContentBrowserSingleton.GetShowPrivateContentPermissionList();
|
|
|
|
ShowPrivateContentPermissionList->RemoveAllowListItem(Owner, InvariantPath);
|
|
|
|
ContentBrowserSingleton.SetPrivateContentPermissionListDirty();
|
|
}
|
|
|
|
FAutoConsoleVariable CVarShowCustomVirtualFolderIcon(
|
|
TEXT("ContentBrowser.ShowCustomVirtualFolderIcon"),
|
|
1,
|
|
TEXT("Whether to show a special icon for custom virtual folders added for organizational purposes in the content browser. E.g. EditorCustomVirtualPath field in plugins"));
|
|
|
|
bool ContentBrowserUtils::ShouldShowCustomVirtualFolderIcon()
|
|
{
|
|
return CVarShowCustomVirtualFolderIcon->GetBool();
|
|
}
|
|
|
|
FAutoConsoleVariable CVarShowPluginFolderIcon(
|
|
TEXT("ContentBrowser.ShowPluginFolderIcon"),
|
|
1,
|
|
TEXT("Whether to show a special icon for plugin folders in the content browser."));
|
|
|
|
bool ContentBrowserUtils::ShouldShowPluginFolderIcon()
|
|
{
|
|
return CVarShowPluginFolderIcon->GetBool();
|
|
}
|
|
|
|
bool ContentBrowserUtils::ShouldShowRedirectors(TSharedPtr<SFilterList> Filters)
|
|
{
|
|
if (Filters.IsValid())
|
|
{
|
|
TSharedPtr<FFrontendFilter> ShowRedirectorsFilter = Filters->GetFrontendFilter(TEXT("ShowRedirectorsBackend"));
|
|
if (ShowRedirectorsFilter.IsValid())
|
|
{
|
|
return Filters->IsFrontendFilterActive(ShowRedirectorsFilter);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FContentBrowserInstanceConfig* ContentBrowserUtils::GetContentBrowserConfig(FName InstanceName)
|
|
{
|
|
if (InstanceName.IsNone())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
FContentBrowserInstanceConfig* Config = UContentBrowserConfig::Get()->Instances.Find(InstanceName);
|
|
if (Config == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return Config;
|
|
}
|
|
|
|
FPathViewConfig* ContentBrowserUtils::GetPathViewConfig(FName InstanceName)
|
|
{
|
|
if (InstanceName.IsNone())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
FContentBrowserInstanceConfig* Config = UContentBrowserConfig::Get()->Instances.Find(InstanceName);
|
|
if (Config == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return &Config->PathView;
|
|
}
|
|
|
|
EContentBrowserItemAttributeFilter ContentBrowserUtils::GetContentBrowserItemAttributeFilter(FName InstanceName)
|
|
{
|
|
const UContentBrowserSettings* ContentBrowserSettings = GetDefault<UContentBrowserSettings>();
|
|
bool bDisplayEngineContent = ContentBrowserSettings->GetDisplayEngineFolder();
|
|
bool bDisplayPluginContent = ContentBrowserSettings->GetDisplayPluginFolders();
|
|
bool bDisplayDevelopersContent = ContentBrowserSettings->GetDisplayDevelopersFolder();
|
|
bool bDisplayL10NContent = ContentBrowserSettings->GetDisplayL10NFolder();
|
|
|
|
// check to see if we have an instance config that overrides the defaults in UContentBrowserSettings
|
|
if (FContentBrowserInstanceConfig* EditorConfig = GetContentBrowserConfig(InstanceName))
|
|
{
|
|
bDisplayEngineContent = EditorConfig->bShowEngineContent;
|
|
bDisplayPluginContent = EditorConfig->bShowPluginContent;
|
|
bDisplayDevelopersContent = EditorConfig->bShowDeveloperContent;
|
|
bDisplayL10NContent = EditorConfig->bShowLocalizedContent;
|
|
}
|
|
|
|
return EContentBrowserItemAttributeFilter::IncludeProject
|
|
| (bDisplayEngineContent ? EContentBrowserItemAttributeFilter::IncludeEngine : EContentBrowserItemAttributeFilter::IncludeNone)
|
|
| (bDisplayPluginContent ? EContentBrowserItemAttributeFilter::IncludePlugins : EContentBrowserItemAttributeFilter::IncludeNone)
|
|
| (bDisplayDevelopersContent ? EContentBrowserItemAttributeFilter::IncludeDeveloper : EContentBrowserItemAttributeFilter::IncludeNone)
|
|
| (bDisplayL10NContent ? EContentBrowserItemAttributeFilter::IncludeLocalized : EContentBrowserItemAttributeFilter::IncludeNone);
|
|
}
|
|
|
|
FContentBrowserItem ContentBrowserUtils::TryGetItemFromUserProvidedPath(FStringView RequestedPathView)
|
|
{
|
|
// For all types of accepted input we can trim a trailing slash if it exists
|
|
if (RequestedPathView.EndsWith('/'))
|
|
{
|
|
RequestedPathView.LeftChopInline(1);
|
|
}
|
|
|
|
UContentBrowserDataSubsystem* ContentBrowserData = IContentBrowserDataModule::Get().GetSubsystem();
|
|
FName RequestedPath(RequestedPathView);
|
|
|
|
// If the path is already a valid virtual path, go there
|
|
FContentBrowserItem Item = ContentBrowserData->GetItemAtPath(RequestedPath, EContentBrowserItemTypeFilter::IncludeAll);
|
|
if (Item.IsValid())
|
|
{
|
|
return Item;
|
|
}
|
|
|
|
// If the path is a non-virtual path like /Game/Maps transform it into a virtual path and try and find an item there
|
|
FName VirtualPath = ContentBrowserData->ConvertInternalPathToVirtual(RequestedPath);
|
|
if (!VirtualPath.IsNone())
|
|
{
|
|
Item = ContentBrowserData->GetItemAtPath(VirtualPath, EContentBrowserItemTypeFilter::IncludeAll);
|
|
if (Item.IsValid())
|
|
{
|
|
return Item;
|
|
}
|
|
}
|
|
|
|
// If the string is a complete object path (with or without class), sync to that asset
|
|
FStringView ObjectPathView = RequestedPathView;
|
|
if (FPackageName::IsValidObjectPath(ObjectPathView) || FPackageName::ParseExportTextPath(RequestedPathView, nullptr, &ObjectPathView))
|
|
{
|
|
ContentBrowserData->ConvertInternalPathToVirtual(ObjectPathView, VirtualPath);
|
|
Item = ContentBrowserData->GetItemAtPath(VirtualPath, EContentBrowserItemTypeFilter::IncludeFiles);
|
|
if (Item.IsValid())
|
|
{
|
|
return Item;
|
|
}
|
|
}
|
|
|
|
const IAssetTools& AssetTools = IAssetTools::Get();
|
|
UE::Core::FVersePath VersePath;
|
|
if (AssetTools.ShowingContentVersePath())
|
|
{
|
|
UE::Core::FVersePath::TryMake(VersePath, FString(RequestedPathView));
|
|
}
|
|
|
|
// If the string is a complete Verse path to an asset, sync to that asset
|
|
if (VersePath.IsValid())
|
|
{
|
|
FAssetData AssetData = AssetTools.FindAssetByVersePath(VersePath);
|
|
if (AssetData.IsValid())
|
|
{
|
|
FNameBuilder NameBuilder;
|
|
AssetData.GetSoftObjectPath().ToString(NameBuilder);
|
|
|
|
ContentBrowserData->ConvertInternalPathToVirtual(NameBuilder, VirtualPath);
|
|
Item = ContentBrowserData->GetItemAtPath(VirtualPath, EContentBrowserItemTypeFilter::IncludeFiles);
|
|
if (Item.IsValid())
|
|
{
|
|
return Item;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto GetItemFromPackageName = [ContentBrowserData](const FString& PackageName) -> FContentBrowserItem {
|
|
// Packages like /Game/Characters/Knight do not map to virtual paths in data source, assets like /Game/Characters/Knight.Knight do.
|
|
// See if there's an item if we duplicate the last part of the path
|
|
TStringBuilder<320> InternalPath(InPlace, PackageName, TEXTVIEW("."), FPackageName::GetShortName(PackageName));
|
|
FName VirtualPath;
|
|
ContentBrowserData->ConvertInternalPathToVirtual(InternalPath, VirtualPath);
|
|
FContentBrowserItem Item = ContentBrowserData->GetItemAtPath(VirtualPath, EContentBrowserItemTypeFilter::IncludeFiles);
|
|
if (Item.IsValid())
|
|
{
|
|
return Item;
|
|
}
|
|
|
|
// Otherwise go up to the package path and enumerate items to see if there's an asset with the desired package name
|
|
FString PackagePath = FPackageName::GetLongPackagePath(PackageName);
|
|
ContentBrowserData->ConvertInternalPathToVirtual(PackagePath, VirtualPath);
|
|
FContentBrowserDataFilter Filter;
|
|
Filter.bRecursivePaths = false;
|
|
Filter.ItemTypeFilter = EContentBrowserItemTypeFilter::IncludeFiles;
|
|
ContentBrowserData->EnumerateItemsUnderPath(VirtualPath, Filter, [&Item, &PackageName](FContentBrowserItem&& InItem) {
|
|
FName InternalPath = InItem.GetInternalPath();
|
|
if (WriteToString<256>(InternalPath).ToView().StartsWith(PackageName))
|
|
{
|
|
Item = MoveTemp(InItem);
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
if (Item.IsValid())
|
|
{
|
|
return Item;
|
|
}
|
|
return FContentBrowserItem();
|
|
};
|
|
|
|
// If the string is an incomplete virtual path that looks more like a package name
|
|
// e.g. /All/Game/Maps/Arena rather than /Game/Maps/Arena or /All/Game/Maps/Arena.Arena
|
|
// try and convert it to an internal path, then try and use it as a package name
|
|
{
|
|
FName ConvertedPath;
|
|
FString PackageName;
|
|
if (ContentBrowserData->TryConvertVirtualPath(RequestedPath, ConvertedPath) == EContentBrowserPathType::Internal)
|
|
{
|
|
if (FPackageName::IsValidLongPackageName(WriteToString<256>(ConvertedPath)))
|
|
{
|
|
PackageName = ConvertedPath.ToString();
|
|
Item = GetItemFromPackageName(PackageName);
|
|
if (Item.IsValid())
|
|
{
|
|
return Item;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the string is a filesystem path to a package, sync to that asset
|
|
FString PackageName;
|
|
if (FPackageName::IsValidLongPackageName(PackageName) || FPackageName::TryConvertFilenameToLongPackageName(FString(RequestedPathView), PackageName))
|
|
{
|
|
Item = GetItemFromPackageName(PackageName);
|
|
if (Item.IsValid())
|
|
{
|
|
return Item;
|
|
}
|
|
}
|
|
|
|
// If the string is a complete Verse path to a module, sync to that folder
|
|
if (VersePath.IsValid())
|
|
{
|
|
IPluginManager::Get().EnumerateVersePathMountPoints(VersePath, [ContentBrowserData, &VirtualPath, &Item](FStringView MountedAssetPath, FStringView LeafPath)
|
|
{
|
|
FNameBuilder NameBuilder;
|
|
NameBuilder.Append(MountedAssetPath);
|
|
if (ensure(NameBuilder.Len() > 0 && NameBuilder.LastChar() == TEXT('/')))
|
|
{
|
|
if (LeafPath.IsEmpty())
|
|
{
|
|
// ContentBrowserData expects no trailing /.
|
|
NameBuilder.RemoveSuffix(1);
|
|
}
|
|
else
|
|
{
|
|
NameBuilder.Append(LeafPath);
|
|
}
|
|
|
|
ContentBrowserData->ConvertInternalPathToVirtual(NameBuilder, VirtualPath);
|
|
Item = ContentBrowserData->GetItemAtPath(VirtualPath, EContentBrowserItemTypeFilter::IncludeFolders);
|
|
}
|
|
return !Item.IsValid();
|
|
});
|
|
|
|
if (Item.IsValid())
|
|
{
|
|
return Item;
|
|
}
|
|
}
|
|
|
|
// Try and remove elements from the end of the path until it's a valid virtual path
|
|
FPathViews::IterateAncestors(RequestedPathView, [RequestedPathView, ContentBrowserData, &Item](FStringView InAncestor){
|
|
if (RequestedPathView == InAncestor)
|
|
{
|
|
return true;
|
|
}
|
|
FName AncestorName(InAncestor);
|
|
Item = ContentBrowserData->GetItemAtPath(AncestorName, EContentBrowserItemTypeFilter::IncludeFolders);
|
|
if (Item.IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
if (Item.IsValid())
|
|
{
|
|
return Item;
|
|
}
|
|
|
|
return FContentBrowserItem();
|
|
}
|
|
|
|
FString ContentBrowserUtils::FormatCollectionCrumbData(const ICollectionContainer& CollectionContainer, const FCollectionNameType& Collection)
|
|
{
|
|
return FString::Printf(
|
|
TEXT("%s?%s?%s"),
|
|
*CollectionContainer.GetCollectionSource()->GetName().ToString(),
|
|
*Collection.Name.ToString(),
|
|
*FString::FromInt(Collection.Type));
|
|
}
|
|
|
|
void ContentBrowserUtils::UpdateNavigationBar(const TSharedPtr<SNavigationBar>& NavigationBar, const TSharedPtr<SAssetView>& AssetView, const TSharedPtr<SPathView>& PathView)
|
|
{
|
|
const FAssetViewContentSources& ContentSources = AssetView->GetContentSources();
|
|
|
|
NavigationBar->ClearPaths();
|
|
|
|
if (ContentSources.HasVirtualPaths())
|
|
{
|
|
UContentBrowserDataSubsystem* ContentBrowserData = IContentBrowserDataModule::Get().GetSubsystem();
|
|
|
|
TArray<FString> Crumbs;
|
|
ContentSources.GetVirtualPaths()[0].ToString().ParseIntoArray(Crumbs, TEXT("/"), true);
|
|
|
|
FContentBrowserDataFilter SubItemsFilter;
|
|
SubItemsFilter.ItemTypeFilter = EContentBrowserItemTypeFilter::IncludeFolders;
|
|
SubItemsFilter.bRecursivePaths = false;
|
|
SubItemsFilter.ItemCategoryFilter = PathView->GetContentBrowserItemCategoryFilter();
|
|
SubItemsFilter.ItemAttributeFilter = PathView->GetContentBrowserItemAttributeFilter();
|
|
|
|
FString CrumbPath = TEXT("/");
|
|
for (const FString& Crumb : Crumbs)
|
|
{
|
|
CrumbPath += Crumb;
|
|
|
|
bool bHasSubItems = false;
|
|
ContentBrowserData->EnumerateItemsUnderPath(*CrumbPath, SubItemsFilter, [&bHasSubItems](FContentBrowserItemData&& InSubItem)
|
|
{
|
|
bHasSubItems = true;
|
|
return false;
|
|
});
|
|
|
|
const FContentBrowserItem CrumbFolderItem = ContentBrowserData->GetItemAtPath(*CrumbPath, EContentBrowserItemTypeFilter::IncludeFolders);
|
|
NavigationBar->PushPath(CrumbFolderItem.IsValid() ? CrumbFolderItem.GetDisplayName() : FText::FromString(Crumb), CrumbPath, bHasSubItems);
|
|
|
|
CrumbPath += TEXT("/");
|
|
}
|
|
}
|
|
else if (ContentSources.HasCollections())
|
|
{
|
|
const FCollectionRef& Collection = ContentSources.GetCollections()[0];
|
|
|
|
TArray<FCollectionNameType> CollectionPathItems;
|
|
|
|
// Walk up the parents of this collection so that we can generate a complete path (this loop also adds the child collection to the array)
|
|
for (TOptional<FCollectionNameType> CurrentCollection = FCollectionNameType(Collection.Name, Collection.Type);
|
|
CurrentCollection.IsSet();
|
|
CurrentCollection = Collection.Container->GetParentCollection(CurrentCollection->Name, CurrentCollection->Type))
|
|
{
|
|
CollectionPathItems.Insert(CurrentCollection.GetValue(), 0);
|
|
}
|
|
|
|
// Now add each part of the path to the breadcrumb trail
|
|
for (const FCollectionNameType& CollectionPathItem : CollectionPathItems)
|
|
{
|
|
const FString CrumbData = FormatCollectionCrumbData(*Collection.Container, CollectionPathItem);
|
|
|
|
TArray<FCollectionNameType> ChildCollections;
|
|
Collection.Container->GetChildCollections(CollectionPathItem.Name, CollectionPathItem.Type, ChildCollections);
|
|
const bool bHasChildren = ChildCollections.Num() > 0;
|
|
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("CollectionName"), FText::FromName(CollectionPathItem.Name));
|
|
const FText DisplayName = FText::Format(LOCTEXT("CollectionPathIndicator", "{CollectionName} (Collection)"), Args);
|
|
|
|
NavigationBar->PushPath(DisplayName, CrumbData, bHasChildren);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NavigationBar->PushPath(LOCTEXT("AllAssets", "All Assets"), TEXT(""), true);
|
|
}
|
|
}
|
|
|
|
TArray<FContentBrowserItem> ContentBrowserUtils::FilterOrAliasItems(TArrayView<const FContentBrowserItem> Items)
|
|
{
|
|
UContentBrowserDataSubsystem* ContentBrowserData = IContentBrowserDataModule::Get().GetSubsystem();
|
|
const TSharedRef<FPathPermissionList>& FolderPermissions = FAssetToolsModule::GetModule().Get().GetFolderPermissionList();
|
|
|
|
// If any of the items to sync don't pass the permission filter, try to find an alias that might be visible
|
|
TArray<FContentBrowserItem> NewItems;
|
|
for (const FContentBrowserItem& Item : Items)
|
|
{
|
|
if (FolderPermissions->PassesStartsWithFilter(Item.GetInternalPath()))
|
|
{
|
|
NewItems.Add(Item);
|
|
}
|
|
else
|
|
{
|
|
TArray<FContentBrowserItemPath> Aliases = ContentBrowserData->GetAliasesForPath(Item.GetInternalPath());
|
|
for (const FContentBrowserItemPath& Alias : Aliases)
|
|
{
|
|
if (FolderPermissions->PassesStartsWithFilter(Alias.GetInternalPathName()))
|
|
{
|
|
FContentBrowserItem AliasedItem = ContentBrowserData->GetItemAtPath(Alias.GetVirtualPathName(), EContentBrowserItemTypeFilter::IncludeFiles);
|
|
if (AliasedItem.IsValid())
|
|
{
|
|
NewItems.Add(MoveTemp(AliasedItem));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NewItems;
|
|
}
|
|
|
|
const FContentBrowserInstanceConfig* ContentBrowserUtils::GetConstInstanceConfig(const FName& ForInstance)
|
|
{
|
|
if (ForInstance.IsNone())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
UContentBrowserConfig* Config = UContentBrowserConfig::Get();
|
|
if (Config == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
const FContentBrowserInstanceConfig* InstanceConfig = Config->Instances.Find(ForInstance);
|
|
return InstanceConfig;
|
|
}
|
|
|
|
TArray<FContentBrowserItem> ContentBrowserUtils::GetChildItemsFromVirtualPath(
|
|
const FName& Path,
|
|
EContentBrowserItemCategoryFilter ItemCategoryFilter,
|
|
EContentBrowserItemAttributeFilter ItemAttributeFilter,
|
|
const FName& ConfigInstanceName,
|
|
const SPathView& PathViewForFiltering
|
|
)
|
|
{
|
|
UContentBrowserDataSubsystem* ContentBrowserData = IContentBrowserDataModule::Get().GetSubsystem();
|
|
|
|
const UContentBrowserSettings* ContentBrowserSettings = GetDefault<UContentBrowserSettings>();
|
|
bool bDisplayEmpty = ContentBrowserSettings->DisplayEmptyFolders;
|
|
// check to see if we have an instance config that overrides the default in UContentBrowserSettings
|
|
if (const FContentBrowserInstanceConfig* EditorConfig = GetConstInstanceConfig(ConfigInstanceName))
|
|
{
|
|
bDisplayEmpty = EditorConfig->bShowEmptyFolders;
|
|
}
|
|
|
|
EContentBrowserIsFolderVisibleFlags FolderFlags = ContentBrowserUtils::GetIsFolderVisibleFlags(bDisplayEmpty);
|
|
|
|
FContentBrowserFolderContentsFilter FolderFilter;
|
|
if (bDisplayEmpty)
|
|
{
|
|
FolderFilter.HideFolderIfEmptyFilter = ContentBrowserData->CreateHideFolderIfEmptyFilter();
|
|
}
|
|
else
|
|
{
|
|
FolderFilter.ItemCategoryFilter = ItemCategoryFilter;
|
|
}
|
|
|
|
FContentBrowserDataFilter SubItemsFilter;
|
|
SubItemsFilter.ItemTypeFilter = EContentBrowserItemTypeFilter::IncludeFolders;
|
|
SubItemsFilter.bRecursivePaths = false;
|
|
SubItemsFilter.ItemCategoryFilter = ItemCategoryFilter;
|
|
SubItemsFilter.ItemAttributeFilter = ItemAttributeFilter;
|
|
|
|
TArray<FContentBrowserItem> SubItems = ContentBrowserData->GetItemsUnderPath(Path, SubItemsFilter);
|
|
|
|
for (auto It = SubItems.CreateIterator(); It; ++It)
|
|
{
|
|
const FContentBrowserItem& Item = *It;
|
|
if (!Item.GetInternalPath().IsNone())
|
|
{
|
|
if (!PathViewForFiltering.InternalPathPassesBlockLists(FNameBuilder(Item.GetInternalPath())))
|
|
{
|
|
It.RemoveCurrent();
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Test if any child internal paths pass for this fully virtual path
|
|
bool bPasses = false;
|
|
for (const FContentBrowserItemData& ItemData : Item.GetInternalItems())
|
|
{
|
|
UContentBrowserDataSource* ItemDataSource = ItemData.GetOwnerDataSource();
|
|
if (!ItemDataSource)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
auto OnSubPath = [&PathViewForFiltering, &bPasses, &SubItemsFilter](FName VirtualSubPath, FName InternalPath)
|
|
{
|
|
if (InternalPath.IsNone())
|
|
{
|
|
// Keep enumerating, this path only exist virtually e.g. /All/Plugins
|
|
return true;
|
|
}
|
|
|
|
const FNameBuilder InternalPathBuilder(InternalPath);
|
|
if (ContentBrowserDataUtils::PathPassesAttributeFilter(InternalPathBuilder, 0, SubItemsFilter.ItemAttributeFilter)
|
|
&& PathViewForFiltering.InternalPathPassesBlockLists(InternalPathBuilder))
|
|
{
|
|
bPasses = true;
|
|
// Stop enumerating
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
ItemDataSource->GetRootPathVirtualTree().EnumerateSubPaths(Item.GetVirtualPath(), OnSubPath, /*bRecurse*/ true);
|
|
|
|
if (bPasses)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bPasses)
|
|
{
|
|
It.RemoveCurrent();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!ContentBrowserData->IsFolderVisible(Item.GetVirtualPath(), FolderFlags, FolderFilter))
|
|
{
|
|
It.RemoveCurrent();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return SubItems;
|
|
}
|
|
|
|
TSharedPtr<SWidget> ContentBrowserUtils::GetFolderWidgetForNavigationBar(const FText& InFolderName, const FName& InFolderBrushName, const FLinearColor& InFolderColor)
|
|
{
|
|
const FSlateBrush* FolderBrush = FAppStyle::GetBrush(InFolderBrushName);
|
|
|
|
if (FolderBrush != FAppStyle::GetDefaultBrush())
|
|
{
|
|
return SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(FMargin(2, 0, 6, 0))
|
|
[
|
|
SNew(SBox)
|
|
.WidthOverride(16.f)
|
|
.HeightOverride(16.f)
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(SImage)
|
|
.DesiredSizeOverride(FVector2D(16, 16))
|
|
.Image(FolderBrush)
|
|
.ColorAndOpacity(InFolderColor)
|
|
]
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.f)
|
|
.Padding(FMargin(2, 0, 6, 0))
|
|
.VAlign( VAlign_Center )
|
|
[
|
|
SNew(STextBlock)
|
|
.TextStyle(FAppStyle::Get(), TEXT("Menu.Label"))
|
|
.Text(InFolderName)
|
|
];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|