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

1558 lines
48 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ContentBrowserSingleton.h"
#include "Algo/Transform.h"
#include "AssetRegistry/AssetData.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetToolsModule.h"
#include "AssetViewUtils.h"
#include "CollectionAssetRegistryBridge.h"
#include "Containers/Set.h"
#include "Containers/StringView.h"
#include "ContentBrowserCommands.h"
#include "ContentBrowserDataSubsystem.h"
#include "ContentBrowserModule.h"
#include "ContentBrowserStyle.h"
#include "ContentBrowserUtils.h"
#include "ContentSources/IContentSource.h"
#include "CoreGlobals.h"
#include "Delegates/Delegate.h"
#include "Editor.h"
#include "Editor/EditorEngine.h"
#include "EditorDirectories.h"
#include "EngineLogs.h"
#include "Filters/CustomTextFilters.h"
#include "Framework/Application/SlateApplication.h"
#include "Framework/Commands/UIAction.h"
#include "Framework/Commands/UICommandInfo.h"
#include "Framework/Docking/TabManager.h"
#include "Framework/Docking/WorkspaceItem.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Framework/Notifications/NotificationManager.h"
#include "IAssetTools.h"
#include "IContentBrowserDataModule.h"
#include "IDocumentation.h"
#include "Interfaces/IMainFrameModule.h"
#include "Internationalization/Internationalization.h"
#include "Logging/LogCategory.h"
#include "Logging/LogMacros.h"
#include "Math/Vector2D.h"
#include "Misc/AssertionMacros.h"
#include "Misc/Attribute.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/NamePermissionList.h"
#include "Misc/OutputDeviceRedirector.h"
#include "Misc/PackageName.h"
#include "Misc/StringBuilder.h"
#include "Modules/ModuleManager.h"
#include "SAssetDialog.h"
#include "SAssetPicker.h"
#include "SCollectionPicker.h"
#include "SContentBrowser.h"
#include "SPathPicker.h"
#include "StatusBarSubsystem.h"
#include "Styling/AppStyle.h"
#include "Styling/ISlateStyle.h"
#include "Templates/UnrealTemplate.h"
#include "Textures/SlateIcon.h"
#include "ToolMenu.h"
#include "ToolMenuDelegates.h"
#include "ToolMenuEntry.h"
#include "ToolMenuMisc.h"
#include "ToolMenuSection.h"
#include "ToolMenus.h"
#include "Trace/Detail/Channel.h"
#include "TutorialMetaData.h"
#include "UObject/Class.h"
#include "UObject/PropertyPortFlags.h"
#include "UObject/UnrealNames.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Widgets/SWidget.h"
#include "Widgets/SWindow.h"
#include "WorkspaceMenuStructure.h"
#include "WorkspaceMenuStructureModule.h"
class UObject;
struct FContentBrowserItem;
#define LOCTEXT_NAMESPACE "ContentBrowser"
static const FName ContentBrowserDrawerInstanceName("ContentBrowserDrawer");
IContentBrowserSingleton& IContentBrowserSingleton::Get()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
return ContentBrowserModule.Get();
}
FContentBrowserSingleton::FContentBrowserSingleton()
: CollectionAssetRegistryBridge(MakeShared<FCollectionAssetRegistryBridge>())
, SettingsStringID(0)
{
const FSlateIcon ContentBrowserIcon(UE::ContentBrowser::Private::FContentBrowserStyle::Get().GetStyleSetName(), "ContentBrowser.TabIcon");
const IWorkspaceMenuStructure& MenuStructure = WorkspaceMenu::GetMenuStructure();
TSharedRef<FWorkspaceItem> ContentBrowserGroup = MenuStructure.GetLevelEditorCategory()->AddGroup(
"ContentBrowser",
LOCTEXT("WorkspaceMenu_ContentBrowserCategory", "Content Browser"),
LOCTEXT("ContentBrowserMenuTooltipText", "Open a Content Browser tab."),
ContentBrowserIcon,
true);
for (int32 BrowserIdx = 0; BrowserIdx < UE_ARRAY_COUNT(ContentBrowserTabIDs); BrowserIdx++)
{
const FName TabID = FName(*FString::Printf(TEXT("ContentBrowserTab%d"), BrowserIdx + 1));
ContentBrowserTabIDs[BrowserIdx] = TabID;
const FText DefaultDisplayName = GetContentBrowserLabelWithIndex(BrowserIdx);
FTabSpawnerEntry& ContentBrowserTabSpawner = FGlobalTabmanager::Get()->RegisterNomadTabSpawner(TabID, FOnSpawnTab::CreateRaw(this, &FContentBrowserSingleton::SpawnContentBrowserTab, BrowserIdx))
.SetDisplayName(DefaultDisplayName)
.SetTooltipText(LOCTEXT("ContentBrowserMenuTooltipText", "Open a Content Browser tab."))
.SetGroup(ContentBrowserGroup)
.SetIcon(ContentBrowserIcon);
ContentBrowserTabs.Add(ContentBrowserTabSpawner.AsSpawnerEntry());
}
UToolMenu* ContentMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.AddQuickMenu");
FToolMenuSection& Section = ContentMenu->FindOrAddSection("Content");
Section.AddSubMenu("ContentBrowser", LOCTEXT("ContentBrowserMenu", "Content Browser"), LOCTEXT("ContentBrowserTooltip", "Actions related to the Content Browser"),
FNewToolMenuDelegate::CreateRaw(this, &FContentBrowserSingleton::GetContentBrowserSubMenu, ContentBrowserGroup), false,
FSlateIcon(FAppStyle::Get().GetStyleSetName(), "LevelEditor.OpenContentBrowser"))
.InsertPosition.Position = EToolMenuInsertType::Last;
// Register to be notified when properties are edited
FEditorDelegates::LoadSelectedAssetsIfNeeded.AddRaw(this, &FContentBrowserSingleton::OnEditorLoadSelectedAssetsIfNeeded);
FContentBrowserCommands::Register();
PopulateConfigValues();
ShowPrivateContentState.InvariantPaths = MakeShared<FPathPermissionList>();
ShowPrivateContentState.InvariantPaths->OnFilterChanged().AddRaw(this, &FContentBrowserSingleton::SetPrivateContentPermissionListDirty);
ShowPrivateContentState.CachedVirtualPaths = MakeShared<FPathPermissionList>();
ShowEpicInternalContentState.InvariantPaths = MakeShared<FPathPermissionList>();
}
FContentBrowserSingleton::~FContentBrowserSingleton()
{
FEditorDelegates::LoadSelectedAssetsIfNeeded.RemoveAll(this);
if ( FSlateApplication::IsInitialized() )
{
for ( int32 BrowserIdx = 0; BrowserIdx < UE_ARRAY_COUNT(ContentBrowserTabIDs); BrowserIdx++ )
{
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner( ContentBrowserTabIDs[BrowserIdx] );
}
}
}
TSharedRef<SWidget> FContentBrowserSingleton::CreateAssetPicker(const FAssetPickerConfig& AssetPickerConfig)
{
return SNew( SAssetPicker )
.IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() )
.AssetPickerConfig(AssetPickerConfig);
}
TSharedPtr<SWidget> FContentBrowserSingleton::GetAssetPickerSearchBox(const TSharedRef<SWidget>& AssetPickerWidget)
{
TSharedRef<SAssetPicker> AssetPicker = StaticCastSharedRef<SAssetPicker>(AssetPickerWidget);
return AssetPicker->GetSearchBox();
}
TSharedRef<SWidget> FContentBrowserSingleton::CreatePathPicker(const FPathPickerConfig& PathPickerConfig)
{
return SNew( SPathPicker )
.IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() )
.PathPickerConfig(PathPickerConfig);
}
TSharedRef<class SWidget> FContentBrowserSingleton::CreateCollectionPicker(const FCollectionPickerConfig& CollectionPickerConfig)
{
return SNew( SCollectionPicker )
.IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() )
.CollectionPickerConfig(CollectionPickerConfig);
}
TSharedRef<class SWidget> FContentBrowserSingleton::CreateContentBrowserDrawer(const FContentBrowserConfig& ContentBrowserConfig, TFunction<TSharedPtr<SDockTab>()> InOnGetTabForDrawer)
{
TSharedPtr<SContentBrowser> ContentBrowserDrawerPinned;
if(!ContentBrowserDrawer.IsValid())
{
ContentBrowserDrawerPinned =
SNew(SContentBrowser, ContentBrowserDrawerInstanceName, &ContentBrowserConfig)
.IsEnabled(FSlateApplication::Get().GetNormalExecutionAttribute())
.IsDrawer(true);
OnGetTabForDrawer = InOnGetTabForDrawer;
ContentBrowserDrawer = ContentBrowserDrawerPinned;
}
else
{
ContentBrowserDrawerPinned = ContentBrowserDrawer.Pin();
}
return ContentBrowserDrawerPinned.ToSharedRef();
}
void FContentBrowserSingleton::CreateOpenAssetDialog(const FOpenAssetDialogConfig& InConfig,
const FOnAssetsChosenForOpen& InOnAssetsChosenForOpen,
const FOnAssetDialogCancelled& InOnAssetDialogCancelled)
{
const bool bModal = false;
TSharedRef<SAssetDialog> AssetDialog = SNew(SAssetDialog, InConfig);
AssetDialog->SetOnAssetsChosenForOpen(InOnAssetsChosenForOpen);
AssetDialog->SetOnAssetDialogCancelled(InOnAssetDialogCancelled);
SharedCreateAssetDialogWindow(AssetDialog, InConfig, bModal);
}
TArray<FAssetData> FContentBrowserSingleton::CreateModalOpenAssetDialog(const FOpenAssetDialogConfig& InConfig)
{
struct FModalResults
{
void OnAssetsChosenForOpen(const TArray<FAssetData>& SelectedAssets)
{
SavedResults = SelectedAssets;
}
TArray<FAssetData> SavedResults;
};
FModalResults ModalWindowResults;
FOnAssetsChosenForOpen OnAssetsChosenForOpenDelegate = FOnAssetsChosenForOpen::CreateRaw(&ModalWindowResults, &FModalResults::OnAssetsChosenForOpen);
const bool bModal = true;
TSharedRef<SAssetDialog> AssetDialog = SNew(SAssetDialog, InConfig);
AssetDialog->SetOnAssetsChosenForOpen(OnAssetsChosenForOpenDelegate);
SharedCreateAssetDialogWindow(AssetDialog, InConfig, bModal);
return ModalWindowResults.SavedResults;
}
void FContentBrowserSingleton::CreateSaveAssetDialog(const FSaveAssetDialogConfig& InConfig,
const FOnObjectPathChosenForSave& InOnObjectPathChosenForSave,
const FOnAssetDialogCancelled& InOnAssetDialogCancelled)
{
const bool bModal = false;
TSharedRef<SAssetDialog> AssetDialog = SNew(SAssetDialog, InConfig);
AssetDialog->SetOnObjectPathChosenForSave(InOnObjectPathChosenForSave);
AssetDialog->SetOnAssetDialogCancelled(InOnAssetDialogCancelled);
SharedCreateAssetDialogWindow(AssetDialog, InConfig, bModal);
}
FString FContentBrowserSingleton::CreateModalSaveAssetDialog(const FSaveAssetDialogConfig& InConfig)
{
struct FModalResults
{
void OnObjectPathChosenForSave(const FString& ObjectPath)
{
SavedResult = ObjectPath;
}
FString SavedResult;
};
FModalResults ModalWindowResults;
FOnObjectPathChosenForSave OnObjectPathChosenForSaveDelegate = FOnObjectPathChosenForSave::CreateRaw(&ModalWindowResults, &FModalResults::OnObjectPathChosenForSave);
const bool bModal = true;
TSharedRef<SAssetDialog> AssetDialog = SNew(SAssetDialog, InConfig);
AssetDialog->SetOnObjectPathChosenForSave(OnObjectPathChosenForSaveDelegate);
SharedCreateAssetDialogWindow(AssetDialog, InConfig, bModal);
return ModalWindowResults.SavedResult;
}
bool FContentBrowserSingleton::HasPrimaryContentBrowser() const
{
if ( PrimaryContentBrowser.IsValid() )
{
// There is a primary content browser
return true;
}
else
{
for (int32 BrowserIdx = 0; BrowserIdx < AllContentBrowsers.Num(); ++BrowserIdx)
{
if ( AllContentBrowsers[BrowserIdx].IsValid() )
{
// There is at least one valid content browser
return true;
}
}
// There were no valid content browsers
return false;
}
}
bool FContentBrowserSingleton::SetPrimaryContentBrowser(FName InstanceName)
{
for (int32 BrowserIdx = 0; BrowserIdx < AllContentBrowsers.Num(); ++BrowserIdx)
{
TSharedPtr<SContentBrowser> ContentBrowser = AllContentBrowsers[BrowserIdx].Pin();
if ( ContentBrowser && ContentBrowser->GetInstanceName() == InstanceName)
{
// There is at least one valid content browser
PrimaryContentBrowser = ContentBrowser;
return true;
}
}
return false;
}
void FContentBrowserSingleton::FocusPrimaryContentBrowser(bool bFocusSearch)
{
// See if the primary content browser is still valid
if ( !PrimaryContentBrowser.IsValid() )
{
ChooseNewPrimaryBrowser();
}
if ( PrimaryContentBrowser.IsValid() )
{
FocusContentBrowser( PrimaryContentBrowser.Pin() );
}
else
{
// If we couldn't find a primary content browser, open one
SummonNewBrowser();
}
// Do we also want to focus on the search box of the content browser?
if ( bFocusSearch && PrimaryContentBrowser.IsValid() )
{
PrimaryContentBrowser.Pin()->SetKeyboardFocusOnSearch();
}
}
void FContentBrowserSingleton::FocusContentBrowserSearchField(TSharedPtr<SWidget> ContentBrowserWidget)
{
TSharedPtr<SContentBrowser> BrowserToFocus;
if (ContentBrowserWidget.IsValid() )
{
for (TWeakPtr<SContentBrowser>& Browser : AllContentBrowsers)
{
if (Browser == ContentBrowserWidget)
{
BrowserToFocus = Browser.Pin();
}
}
}
if (!BrowserToFocus.IsValid() && ContentBrowserDrawer == ContentBrowserWidget)
{
BrowserToFocus = ContentBrowserDrawer.Pin();
}
if (BrowserToFocus.IsValid())
{
BrowserToFocus->SetKeyboardFocusOnSearch();
}
}
void FContentBrowserSingleton::CreateNewAsset(const FString& DefaultAssetName, const FString& PackagePath, UClass* AssetClass, UFactory* Factory)
{
const bool bAllowLockedBrowsers = true;
const FName ContentBrowserInstanceName = NAME_None;
const bool bCreateNewContentBrowser = false;
TSharedPtr<SContentBrowser> ContentBrowserToSync = FindContentBrowserToSync(bAllowLockedBrowsers, ContentBrowserInstanceName, bCreateNewContentBrowser);
if (!ContentBrowserToSync.IsValid())
{
FocusPrimaryContentBrowser(false);
ContentBrowserToSync = PrimaryContentBrowser.Pin();
}
if (ContentBrowserToSync.IsValid())
{
ContentBrowserToSync->CreateNewAsset(DefaultAssetName, PackagePath, AssetClass, Factory);
}
}
TSharedPtr<SContentBrowser> FContentBrowserSingleton::FindContentBrowserToSync(bool bAllowLockedBrowsers, const FName& InstanceName, bool bNewSpawnBrowser)
{
TSharedPtr<SContentBrowser> ContentBrowserToSync;
if (InstanceName.IsValid() && !InstanceName.IsNone())
{
for (int32 BrowserIdx = 0; BrowserIdx < AllContentBrowsers.Num(); ++BrowserIdx)
{
if (AllContentBrowsers[BrowserIdx].IsValid() && AllContentBrowsers[BrowserIdx].Pin()->GetInstanceName() == InstanceName)
{
return AllContentBrowsers[BrowserIdx].Pin();
}
}
return ContentBrowserToSync;
}
if ( !PrimaryContentBrowser.IsValid() )
{
ChooseNewPrimaryBrowser();
}
auto CanContentBrowserBeSynced =
[ this, bAllowLockedBrowsers, PinnedDrawer = ContentBrowserDrawer.Pin() ]( const TSharedPtr< SContentBrowser >& ContentBrowser ) -> bool
{
// Content browser can be synced if:
// - It's valid.
// - It's not locked or we allow locked browsers (bAllowLockedBrowsers).
// - If it's the drawer, the active window needs to have a status bar so that it can show the drawer.
bool bCanBeSynced = ContentBrowser.IsValid() && ( bAllowLockedBrowsers || !ContentBrowser->IsLocked() );
if ( ContentBrowser == PinnedDrawer )
{
bCanBeSynced = bCanBeSynced && GEditor->GetEditorSubsystem<UStatusBarSubsystem>()->ActiveWindowHasStatusBar();
}
return bCanBeSynced;
};
if ( CanContentBrowserBeSynced( PrimaryContentBrowser.Pin() ) )
{
// If wanting to spawn a new browser window, don't set the BrowserToSync in order to summon a new browser
if (!bNewSpawnBrowser)
{
// If the primary content browser is not locked, sync it
ContentBrowserToSync = PrimaryContentBrowser.Pin();
}
}
else
{
// If there is no primary or it is locked, sync the content browser drawer if it's available
if ( PrimaryContentBrowser != ContentBrowserDrawer
&& CanContentBrowserBeSynced( ContentBrowserDrawer.Pin() ) )
{
ContentBrowserToSync = ContentBrowserDrawer.Pin();
}
else
{
// if the drawer isn't available, find the first non-locked valid browser
for ( const TWeakPtr< SContentBrowser >& ContentBrowser : AllContentBrowsers )
{
if ( CanContentBrowserBeSynced( ContentBrowser.Pin() ) )
{
ContentBrowserToSync = ContentBrowser.Pin();
break;
}
}
}
}
if ( !ContentBrowserToSync.IsValid() )
{
// There are no valid, unlocked browsers, attempt to summon a new one.
const FName NewBrowserName = SummonNewBrowser(bAllowLockedBrowsers);
// Now try to find a non-locked valid browser again, now that a new one may exist
for (int32 BrowserIdx = 0; BrowserIdx < AllContentBrowsers.Num(); ++BrowserIdx)
{
if ((AllContentBrowsers[BrowserIdx].IsValid() && (NewBrowserName == NAME_None && (bAllowLockedBrowsers || !AllContentBrowsers[BrowserIdx].Pin()->IsLocked()))) || (AllContentBrowsers[BrowserIdx].IsValid() && AllContentBrowsers[BrowserIdx].Pin()->GetInstanceName() == NewBrowserName))
{
ContentBrowserToSync = AllContentBrowsers[BrowserIdx].Pin();
break;
}
}
}
if ( !ContentBrowserToSync.IsValid() )
{
FText NotificationText = FText::FromString(TEXT("Unable to browse to the requested asset. All Content Browsers are locked."));
FNotificationInfo Notification(NotificationText);
Notification.ExpireDuration = 3.0f;
FSlateNotificationManager::Get().AddNotification(Notification);
UE_LOG(LogNet, Log, TEXT("%s"), *NotificationText.ToString());
}
return ContentBrowserToSync;
}
void FContentBrowserSingleton::SyncBrowserToAssets(const TArray<FAssetData>& AssetDataList, bool bAllowLockedBrowsers, bool bFocusContentBrowser, const FName& InstanceName, bool bNewSpawnBrowser)
{
TSharedPtr<SContentBrowser> ContentBrowserToSync = FindContentBrowserToSync(bAllowLockedBrowsers, InstanceName, bNewSpawnBrowser);
if ( ContentBrowserToSync.IsValid() )
{
// Finally, focus and sync the browser that was found
if (bFocusContentBrowser)
{
FocusContentBrowser(ContentBrowserToSync);
}
TSet<FName> OuterPathNames;
TArray<FAssetData> AssetDataListToSync;
AssetDataListToSync.Reserve(AssetDataList.Num());
for (const FAssetData& AssetData : AssetDataList)
{
if (FName OuterPathName = AssetData.GetOptionalOuterPathName(); !OuterPathName.IsNone())
{
OuterPathNames.Add(*FSoftObjectPath(OuterPathName.ToString()).GetLongPackageName());
}
else
{
AssetDataListToSync.Add(AssetData);
}
}
if (OuterPathNames.Num())
{
TMap<FName, FAssetData> PackagesToAssetDataMap;
UE::AssetRegistry::GetAssetForPackages(OuterPathNames.Array(), PackagesToAssetDataMap);
Algo::Transform(PackagesToAssetDataMap, AssetDataListToSync, [](const TPair<FName, FAssetData>& Pair) { return Pair.Value; });
}
ContentBrowserToSync->SyncToAssets(AssetDataListToSync);
}
}
void FContentBrowserSingleton::SyncBrowserToAssets(const TArray<UObject*>& AssetList, bool bAllowLockedBrowsers, bool bFocusContentBrowser, const FName& InstanceName, bool bNewSpawnBrowser)
{
// Convert UObject* array to FAssetData array
TArray<FAssetData> AssetDataList;
for (int32 AssetIdx = 0; AssetIdx < AssetList.Num(); ++AssetIdx)
{
if ( AssetList[AssetIdx] )
{
AssetDataList.Add( FAssetData(AssetList[AssetIdx]) );
}
}
SyncBrowserToAssets(AssetDataList, bAllowLockedBrowsers, bFocusContentBrowser, InstanceName, bNewSpawnBrowser);
}
void FContentBrowserSingleton::SyncBrowserToFolders(const TArray<FString>& FolderList, bool bAllowLockedBrowsers, bool bFocusContentBrowser, const FName& InstanceName, bool bNewSpawnBrowser)
{
TSharedPtr<SContentBrowser> ContentBrowserToSync = FindContentBrowserToSync(bAllowLockedBrowsers, InstanceName, bNewSpawnBrowser);
if ( ContentBrowserToSync.IsValid() )
{
// Finally, focus and sync the browser that was found
if (bFocusContentBrowser)
{
FocusContentBrowser(ContentBrowserToSync);
}
ContentBrowserToSync->SyncToFolders(FolderList);
}
}
void FContentBrowserSingleton::SyncBrowserToItems(const TArray<FContentBrowserItem>& ItemsToSync, bool bAllowLockedBrowsers, bool bFocusContentBrowser, const FName& InstanceName, bool bNewSpawnBrowser)
{
TSharedPtr<SContentBrowser> ContentBrowserToSync = FindContentBrowserToSync(bAllowLockedBrowsers, InstanceName, bNewSpawnBrowser);
if ( ContentBrowserToSync.IsValid() )
{
// Finally, focus and sync the browser that was found
if (bFocusContentBrowser)
{
FocusContentBrowser(ContentBrowserToSync);
}
ContentBrowserToSync->SyncToItems(ItemsToSync);
}
}
void FContentBrowserSingleton::SyncBrowserTo(const FContentBrowserSelection& ItemSelection, bool bAllowLockedBrowsers, bool bFocusContentBrowser, const FName& InstanceName, bool bNewSpawnBrowser)
{
TSharedPtr<SContentBrowser> ContentBrowserToSync = FindContentBrowserToSync(bAllowLockedBrowsers, InstanceName, bNewSpawnBrowser);
if ( ContentBrowserToSync.IsValid() )
{
// Finally, focus and sync the browser that was found
if (bFocusContentBrowser)
{
FocusContentBrowser(ContentBrowserToSync);
}
ContentBrowserToSync->SyncTo(ItemSelection);
}
}
void FContentBrowserSingleton::GetSelectedAssets(TArray<FAssetData>& SelectedAssets)
{
if ( PrimaryContentBrowser.IsValid() )
{
PrimaryContentBrowser.Pin()->GetSelectedAssets(SelectedAssets);
}
}
void FContentBrowserSingleton::GetSelectedFolders(TArray<FString>& SelectedFolders)
{
if (PrimaryContentBrowser.IsValid())
{
PrimaryContentBrowser.Pin()->GetSelectedFolders(SelectedFolders);
}
}
void FContentBrowserSingleton::GetSelectedPathViewFolders(TArray<FString>& SelectedFolders)
{
if (PrimaryContentBrowser.IsValid())
{
SelectedFolders = PrimaryContentBrowser.Pin()->GetSelectedPathViewFolders();
}
}
FString FContentBrowserSingleton::GetCurrentPath(const EContentBrowserPathType PathType)
{
if (PrimaryContentBrowser.IsValid())
{
return PrimaryContentBrowser.Pin()->GetCurrentPath(PathType);
}
return FString();
}
FContentBrowserItemPath FContentBrowserSingleton::GetCurrentPath()
{
if (PrimaryContentBrowser.IsValid())
{
return FContentBrowserItemPath(PrimaryContentBrowser.Pin()->GetCurrentPath(EContentBrowserPathType::Virtual), EContentBrowserPathType::Virtual);
}
return FContentBrowserItemPath();
}
void FContentBrowserSingleton::CaptureThumbnailFromViewport(FViewport* InViewport, TArray<FAssetData>& SelectedAssets)
{
ContentBrowserUtils::CaptureThumbnailFromViewport(InViewport, SelectedAssets);
}
void FContentBrowserSingleton::OnEditorLoadSelectedAssetsIfNeeded()
{
if ( PrimaryContentBrowser.IsValid() )
{
PrimaryContentBrowser.Pin()->LoadSelectedObjectsIfNeeded();
}
}
FContentBrowserSingleton& FContentBrowserSingleton::Get()
{
static const FName ModuleName = "ContentBrowser";
FContentBrowserModule& Module = FModuleManager::GetModuleChecked<FContentBrowserModule>(ModuleName);
return static_cast<FContentBrowserSingleton&>(Module.Get());
}
void FContentBrowserSingleton::SetPrimaryContentBrowser(const TSharedRef<SContentBrowser>& NewPrimaryBrowser)
{
if ( PrimaryContentBrowser.IsValid() && PrimaryContentBrowser.Pin().ToSharedRef() == NewPrimaryBrowser )
{
// This is already the primary content browser
return;
}
if ( !NewPrimaryBrowser->CanSetAsPrimaryContentBrowser() )
{
// This browser can not be set as primary
return;
}
if ( PrimaryContentBrowser.IsValid() )
{
PrimaryContentBrowser.Pin()->SetIsPrimaryContentBrowser(false);
}
PrimaryContentBrowser = NewPrimaryBrowser;
NewPrimaryBrowser->SetIsPrimaryContentBrowser(true);
}
void FContentBrowserSingleton::ContentBrowserClosed(const TSharedRef<SContentBrowser>& ClosedBrowser)
{
// Find the browser in the all browsers list
for (int32 BrowserIdx = AllContentBrowsers.Num() - 1; BrowserIdx >= 0; --BrowserIdx)
{
if ( !AllContentBrowsers[BrowserIdx].IsValid() || AllContentBrowsers[BrowserIdx].Pin() == ClosedBrowser )
{
AllContentBrowsers.RemoveAt(BrowserIdx);
}
}
if ( !PrimaryContentBrowser.IsValid() || ClosedBrowser == PrimaryContentBrowser.Pin() )
{
ChooseNewPrimaryBrowser();
}
BrowserToLastKnownTabManagerMap.Add(ClosedBrowser->GetInstanceName(), ClosedBrowser->GetTabManager());
}
const FContentBrowserPluginSettings& FContentBrowserSingleton::GetPluginSettings(FName PluginName) const
{
const FContentBrowserPluginSettings* LocalSettings = PluginSettings.FindByPredicate([PluginName](const FContentBrowserPluginSettings& Setting) { return Setting.PluginName == PluginName; });
if (LocalSettings)
{
return *LocalSettings;
}
static FContentBrowserPluginSettings DefaultSettings;
return DefaultSettings;
}
void FContentBrowserSingleton::SharedCreateAssetDialogWindow(const TSharedRef<SAssetDialog>& AssetDialog, const FSharedAssetDialogConfig& InConfig, bool bModal) const
{
const FVector2D DefaultWindowSize(1152.0f, 648.0f);
const FVector2D WindowSize = InConfig.WindowSizeOverride.IsZero() ? DefaultWindowSize : InConfig.WindowSizeOverride;
const FText WindowTitle = InConfig.DialogTitleOverride.IsEmpty() ? LOCTEXT("GenericAssetDialogWindowHeader", "Asset Dialog") : InConfig.DialogTitleOverride;
TSharedRef<SWindow> DialogWindow =
SNew(SWindow)
.Title(WindowTitle)
.ClientSize(WindowSize);
DialogWindow->SetContent(AssetDialog);
TSharedPtr<SWindow> ParentWindow = InConfig.WindowOverride;
if (!InConfig.WindowOverride)
{
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
const TSharedPtr<SWindow>& MainFrameParentWindow = MainFrameModule.GetParentWindow();
ParentWindow = MainFrameModule.GetParentWindow();
}
if (ParentWindow.IsValid())
{
if (bModal)
{
FSlateApplication::Get().AddModalWindow(DialogWindow, ParentWindow.ToSharedRef());
}
else if (FGlobalTabmanager::Get()->GetRootWindow().IsValid())
{
FSlateApplication::Get().AddWindowAsNativeChild(DialogWindow, ParentWindow.ToSharedRef());
}
else
{
FSlateApplication::Get().AddWindow(DialogWindow);
}
}
else
{
if (ensureMsgf(!bModal, TEXT("Could not create asset dialog because modal windows must have a parent and this was called at a time where the mainframe window does not exist.")))
{
FSlateApplication::Get().AddWindow(DialogWindow);
}
}
}
void FContentBrowserSingleton::ChooseNewPrimaryBrowser()
{
// The drawer is the lowest priority. If a docked content browser can become the primary browser choose it first
// Find the first valid browser and trim any invalid browsers along the way
for (int32 BrowserIdx = 0; BrowserIdx < AllContentBrowsers.Num(); ++BrowserIdx)
{
if (TSharedPtr<SContentBrowser> TestBrowser = AllContentBrowsers[BrowserIdx].Pin())
{
if (TestBrowser->CanSetAsPrimaryContentBrowser() && TestBrowser != ContentBrowserDrawer)
{
SetPrimaryContentBrowser(AllContentBrowsers[BrowserIdx].Pin().ToSharedRef());
break;
}
}
else
{
// Trim any invalid content browsers
AllContentBrowsers.RemoveAt(BrowserIdx);
BrowserIdx--;
}
}
// Set the content browser drawer as the primary browser
if (!PrimaryContentBrowser.IsValid() && ContentBrowserDrawer.IsValid())
{
SetPrimaryContentBrowser(ContentBrowserDrawer.Pin().ToSharedRef());
}
}
void FContentBrowserSingleton::FocusContentBrowser(const TSharedPtr<SContentBrowser>& BrowserToFocus)
{
if ( BrowserToFocus.IsValid() )
{
if (BrowserToFocus == ContentBrowserDrawer)
{
GEditor->GetEditorSubsystem<UStatusBarSubsystem>()->OpenContentBrowserDrawer();
}
else
{
TSharedRef<SContentBrowser> Browser = BrowserToFocus.ToSharedRef();
TSharedPtr<FTabManager> TabManager = Browser->GetTabManager();
if (TabManager.IsValid())
{
TabManager->TryInvokeTab(Browser->GetInstanceName());
}
}
}
}
void FContentBrowserSingleton::DockContentBrowserDrawer()
{
TSharedPtr<FTabManager> ForTabManager;
TSharedPtr<SDockTab> Tab = OnGetTabForDrawer();
if (Tab)
{
ForTabManager = FGlobalTabmanager::Get()->GetTabManagerForMajorTab(Tab.ToSharedRef());
}
// Dont summon a content browser if a content browser already exists in the tab manager
bool bExistingTab = false;
if(ForTabManager)
{
for (int32 BrowserIdx = 0; BrowserIdx < UE_ARRAY_COUNT(ContentBrowserTabIDs); BrowserIdx++)
{
if (TSharedPtr<SDockTab> ExistingTab = ForTabManager->FindExistingLiveTab(ContentBrowserTabIDs[BrowserIdx]))
{
GEditor->GetEditorSubsystem<UStatusBarSubsystem>()->ForceDismissDrawer();
ExistingTab->ActivateInParent(ETabActivationCause::SetDirectly);
bExistingTab = true;
break;
}
}
}
if(!bExistingTab)
{
TSharedPtr<SContentBrowser> ContentBrowserDrawerPinned = ContentBrowserDrawer.Pin();
// Make sure current content browser drawer settings are saved so we can copy them to the new browser
ContentBrowserDrawerPinned->SaveSettings();
FName InstanceName = SummonNewBrowser(false, ForTabManager);
for (int32 AllBrowsersIdx = AllContentBrowsers.Num() - 1; AllBrowsersIdx >= 0; --AllBrowsersIdx)
{
if (TSharedPtr<SContentBrowser> Browser = AllContentBrowsers[AllBrowsersIdx].Pin())
{
if (Browser->GetInstanceName() == InstanceName)
{
Browser->CopySettingsFromBrowser(ContentBrowserDrawerPinned);
break;
}
}
}
GEditor->GetEditorSubsystem<UStatusBarSubsystem>()->ForceDismissDrawer();
}
}
FName FContentBrowserSingleton::SummonNewBrowser(bool bAllowLockedBrowsers, TSharedPtr<FTabManager> SpecificTabManager)
{
TSet<FName> OpenBrowserIDs;
// Find all currently open browsers to help find the first open slot
for (int32 BrowserIdx = AllContentBrowsers.Num() - 1; BrowserIdx >= 0; --BrowserIdx)
{
const TWeakPtr<SContentBrowser>& Browser = AllContentBrowsers[BrowserIdx];
if ( Browser.IsValid() )
{
OpenBrowserIDs.Add(Browser.Pin()->GetInstanceName());
}
}
FName NewTabName;
for ( int32 BrowserIdx = 0; BrowserIdx < UE_ARRAY_COUNT(ContentBrowserTabIDs); BrowserIdx++ )
{
FName TestTabID = ContentBrowserTabIDs[BrowserIdx];
if ( !OpenBrowserIDs.Contains(TestTabID) && (bAllowLockedBrowsers || !IsLocked(TestTabID)) )
{
// Found the first index that is not currently open
NewTabName = TestTabID;
break;
}
}
if ( NewTabName != NAME_None )
{
const TWeakPtr<FTabManager>& TabManagerToInvoke = SpecificTabManager.IsValid() ? SpecificTabManager : BrowserToLastKnownTabManagerMap.FindRef(NewTabName);
if ( TabManagerToInvoke.IsValid() )
{
TabManagerToInvoke.Pin()->TryInvokeTab(NewTabName);
}
else
{
FGlobalTabmanager::Get()->TryInvokeTab(NewTabName);
}
}
else
{
// No available slots... don't summon anything
}
return NewTabName;
}
TSharedRef<SWidget> FContentBrowserSingleton::CreateContentBrowser( const FName InstanceName, TSharedPtr<SDockTab> ContainingTab, const FContentBrowserConfig* ContentBrowserConfig )
{
TSharedRef<SContentBrowser> NewBrowser =
SNew( SContentBrowser, InstanceName, ContentBrowserConfig )
.IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() )
.ContainingTab( ContainingTab );
AllContentBrowsers.Add( NewBrowser );
// The drawer is the lowest priority. If a new content browser is being added see if it is capable of becoming the primary browser
if( !PrimaryContentBrowser.IsValid() || PrimaryContentBrowser == ContentBrowserDrawer)
{
ChooseNewPrimaryBrowser();
}
return NewBrowser;
}
TSharedRef<SDockTab> FContentBrowserSingleton::SpawnContentBrowserTab( const FSpawnTabArgs& SpawnTabArgs, int32 BrowserIdx )
{
TAttribute<FText> Label = TAttribute<FText>::Create( TAttribute<FText>::FGetter::CreateRaw( this, &FContentBrowserSingleton::GetContentBrowserTabLabel, BrowserIdx ) );
TSharedRef<SDockTab> NewTab = SNew(SDockTab)
.TabRole(ETabRole::NomadTab)
.Label( Label )
.ToolTip( IDocumentation::Get()->CreateToolTip( Label, nullptr, "Shared/ContentBrowser", "Tab" ) )
.OnExtendContextMenu_Raw(this, &FContentBrowserSingleton::ExtendContentBrowserTabContextMenu);
TSharedRef<SWidget> NewBrowser = CreateContentBrowser( SpawnTabArgs.GetTabId().TabType, NewTab, nullptr );
// Add wrapper for tutorial highlighting
TSharedRef<SBox> Wrapper =
SNew(SBox)
.AddMetaData<FTutorialMetaData>(FTutorialMetaData(TEXT("ContentBrowser"), TEXT("ContentBrowserTab1")))
[
NewBrowser
];
NewTab->SetContent( Wrapper );
return NewTab;
}
void FContentBrowserSingleton::ExtendContentBrowserTabContextMenu(FMenuBuilder& InMenuBuilder)
{
InMenuBuilder.BeginSection("SummonContentBrowserTabs", LOCTEXT("ContentBrowserTabs", "Content Browser Tabs"));
for (int32 BrowserIdx = 0; BrowserIdx < UE_ARRAY_COUNT(ContentBrowserTabIDs); BrowserIdx++)
{
const FName TabID = ContentBrowserTabIDs[BrowserIdx];
const FText DefaultDisplayName = GetContentBrowserLabelWithIndex(BrowserIdx);
InMenuBuilder.AddMenuEntry(DefaultDisplayName,
LOCTEXT("ContentBrowserMenuTooltipText", "Open a Content Browser tab."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateLambda([TabID, this]()
{
// Go through all the content browsers to check if the current one is open
for (int32 BrowserIdx = 0; BrowserIdx < AllContentBrowsers.Num(); ++BrowserIdx)
{
const TWeakPtr<SContentBrowser>& Browser = AllContentBrowsers[BrowserIdx];
if (Browser.IsValid() && Browser.Pin()->GetInstanceName() == TabID)
{
FocusContentBrowser(Browser.Pin()); // Focus it if so
}
}
// If the tab was not found, try to open it
FGlobalTabmanager::Get()->TryInvokeTab(TabID);
}
),
FCanExecuteAction(),
FIsActionChecked::CreateLambda([this, TabID]()
{
// Go through all the content browsers to check if the current one is open
for (int32 BrowserIdx = 0; BrowserIdx < AllContentBrowsers.Num(); ++BrowserIdx)
{
const TWeakPtr<SContentBrowser>& Browser = AllContentBrowsers[BrowserIdx];
if (Browser.IsValid() && Browser.Pin()->GetInstanceName() == TabID)
{
return true;
}
}
return false;
}
)),
NAME_None,
EUserInterfaceActionType::ToggleButton);
}
InMenuBuilder.EndSection();
}
bool FContentBrowserSingleton::IsLocked(const FName& InstanceName) const
{
// First try all the open browsers, as their locked state might be newer than the configs
for (int32 AllBrowsersIdx = AllContentBrowsers.Num() - 1; AllBrowsersIdx >= 0; --AllBrowsersIdx)
{
const TWeakPtr<SContentBrowser>& Browser = AllContentBrowsers[AllBrowsersIdx];
if ( Browser.IsValid() && Browser.Pin()->GetInstanceName() == InstanceName )
{
return Browser.Pin()->IsLocked();
}
}
// Fallback to getting the locked state from the config instead
bool bIsLocked = false;
GConfig->GetBool(*SContentBrowser::SettingsIniSection, *(InstanceName.ToString() + TEXT(".Locked")), bIsLocked, GEditorPerProjectIni);
return bIsLocked;
}
FText FContentBrowserSingleton::GetContentBrowserLabelWithIndex( int32 BrowserIdx )
{
return FText::Format(LOCTEXT("ContentBrowserTabNameWithIndex", "Content Browser {0}"), FText::AsNumber(BrowserIdx + 1));
}
FText FContentBrowserSingleton::GetContentBrowserTabLabel(int32 BrowserIdx)
{
int32 NumOpenContentBrowsers = 0;
for (int32 AllBrowsersIdx = AllContentBrowsers.Num() - 1; AllBrowsersIdx >= 0; --AllBrowsersIdx)
{
const TWeakPtr<SContentBrowser>& Browser = AllContentBrowsers[AllBrowsersIdx];
if ( Browser.IsValid() )
{
NumOpenContentBrowsers++;
}
else
{
AllContentBrowsers.RemoveAt(AllBrowsersIdx);
}
}
if ( NumOpenContentBrowsers > 1 )
{
return GetContentBrowserLabelWithIndex( BrowserIdx );
}
else
{
return LOCTEXT("ContentBrowserTabName", "Content Browser");
}
}
void FContentBrowserSingleton::SetSelectedPaths(const TArray<FString>& FolderPaths, bool bNeedsRefresh/* = false*/, bool bPathsAreVirtual/* = false*/)
{
// Make sure we have a valid browser
if (!PrimaryContentBrowser.IsValid())
{
ChooseNewPrimaryBrowser();
if (!PrimaryContentBrowser.IsValid())
{
SummonNewBrowser();
}
}
if (PrimaryContentBrowser.IsValid())
{
if (bPathsAreVirtual)
{
PrimaryContentBrowser.Pin()->SetSelectedPaths(FolderPaths, bNeedsRefresh);
}
else
{
if (UContentBrowserDataSubsystem* ContentBrowserDataSubsystem = IContentBrowserDataModule::Get().GetSubsystem())
{
PrimaryContentBrowser.Pin()->SetSelectedPaths(ContentBrowserDataSubsystem->ConvertInternalPathsToVirtual(FolderPaths), bNeedsRefresh);
}
}
}
}
void FContentBrowserSingleton::ForceShowPluginContent(bool bEnginePlugin)
{
if (!PrimaryContentBrowser.IsValid())
{
ChooseNewPrimaryBrowser();
if (!PrimaryContentBrowser.IsValid())
{
SummonNewBrowser();
}
}
if (PrimaryContentBrowser.IsValid())
{
PrimaryContentBrowser.Pin()->ForceShowPluginContent(bEnginePlugin);
}
}
void FContentBrowserSingleton::SaveContentBrowserSettings(TSharedPtr<SWidget> ContentBrowserWidget)
{
TSharedPtr<SContentBrowser> BrowserToSave;
if (ContentBrowserWidget.IsValid())
{
for (TWeakPtr<SContentBrowser>& Browser : AllContentBrowsers)
{
if (Browser == ContentBrowserWidget)
{
BrowserToSave = Browser.Pin();
}
}
}
if (!BrowserToSave.IsValid() && ContentBrowserDrawer == ContentBrowserWidget)
{
BrowserToSave = ContentBrowserDrawer.Pin();
}
if (BrowserToSave.IsValid())
{
BrowserToSave->SaveSettings();
}
}
void FContentBrowserSingleton::ExecuteRename(TSharedPtr<SWidget> PickerWidget)
{
if (PickerWidget.IsValid())
{
if (PickerWidget->GetType() == FName(TEXT("SAssetPicker")))
{
TSharedPtr<SAssetPicker> AssetPicker = StaticCastSharedPtr<SAssetPicker>(PickerWidget);
AssetPicker->ExecuteRenameCommand();
}
else if (PickerWidget->GetType() == FName(TEXT("SPathPicker")))
{
TSharedPtr<SPathPicker> PathPicker = StaticCastSharedPtr<SPathPicker>(PickerWidget);
PathPicker->ExecuteRenameFolder();
}
}
}
void FContentBrowserSingleton::ExecuteAddFolder(TSharedPtr<SWidget> Widget)
{
if (Widget.IsValid())
{
if (Widget->GetType() == FName(TEXT("SPathPicker")))
{
TSharedPtr<SPathPicker> PathPicker = StaticCastSharedPtr<SPathPicker>(Widget);
PathPicker->ExecuteAddFolder();
}
}
}
void FContentBrowserSingleton::RefreshPathView(TSharedPtr<SWidget> Widget)
{
if (Widget.IsValid())
{
if (Widget->GetType() == FName(TEXT("SPathPicker")))
{
TSharedPtr<SPathPicker> PathPicker = StaticCastSharedPtr<SPathPicker>(Widget);
PathPicker->RefreshPathView();
}
}
}
bool FContentBrowserSingleton::IsShowingPrivateContent(const FStringView VirtualFolderPath)
{
if (!ShowPrivateContentState.InvariantPaths->HasFiltering())
{
return false;
}
if (!ShowPrivateContentState.CachedVirtualPaths)
{
RebuildPrivateContentStateCache();
}
return ShowPrivateContentState.CachedVirtualPaths->PassesStartsWithFilter(VirtualFolderPath);
}
void FContentBrowserSingleton::RebuildPrivateContentStateCache()
{
ShowPrivateContentState.CachedVirtualPaths.Reset();
ShowPrivateContentState.CachedVirtualPaths = MakeShared<FPathPermissionList>();
TArray<FString> InvariantAllowList = ShowPrivateContentState.InvariantPaths->GetAllowListEntries();
for (const FString& InvariantPath : InvariantAllowList)
{
FName VirtualPath;
IContentBrowserDataModule::Get().GetSubsystem()->ConvertInternalPathToVirtual(FStringView(InvariantPath),
VirtualPath);
ShowPrivateContentState.CachedVirtualPaths->AddAllowListItem(TEXT("ContentBrowser"), VirtualPath);
}
}
bool FContentBrowserSingleton::IsFolderShowPrivateContentToggleable(const FStringView VirtualFolderPath)
{
if (IsFolderShowPrivateContentToggleableDelegate.IsBound())
{
return IsFolderShowPrivateContentToggleableDelegate.Execute(VirtualFolderPath);
}
return true;
}
const TSharedPtr<FPathPermissionList>& FContentBrowserSingleton::GetShowPrivateContentPermissionList()
{
return ShowPrivateContentState.InvariantPaths;
}
const TSharedPtr<FPathPermissionList>& FContentBrowserSingleton::GetShowEpicInternalContentPermissionList()
{
return ShowEpicInternalContentState.InvariantPaths;
}
void FContentBrowserSingleton::SetPrivateContentPermissionListDirty()
{
ShowPrivateContentState.CachedVirtualPaths.Reset();
}
void FContentBrowserSingleton::RegisterIsFolderShowPrivateContentToggleableDelegate(FIsFolderShowPrivateContentToggleableDelegate InIsFolderShowPrivateContentToggleableDelegate)
{
IsFolderShowPrivateContentToggleableDelegate = MoveTemp(InIsFolderShowPrivateContentToggleableDelegate);
}
void FContentBrowserSingleton::UnregisterIsFolderShowPrivateContentToggleableDelegate()
{
IsFolderShowPrivateContentToggleableDelegate = FIsFolderShowPrivateContentToggleableDelegate();
}
FDelegateHandle FContentBrowserSingleton::RegisterOnFavoritesChangedHandler(FOnFavoritesChanged::FDelegate InOnFavoritesChanged)
{
return OnFavoritesChanged.Add(InOnFavoritesChanged);
}
FDelegateHandle FContentBrowserSingleton::RegisterOnFavoritesChangedHandler(FSimpleDelegate InOnFavoritesChanged)
{
return OnFavoritesChanged.AddLambda([InOnFavoritesChanged](const FContentBrowserItemPath&, bool)
{
InOnFavoritesChanged.ExecuteIfBound();
});
}
void FContentBrowserSingleton::UnregisterOnFavoritesChangedDelegate(FDelegateHandle Handle)
{
OnFavoritesChanged.Remove(Handle);
}
void FContentBrowserSingleton::BroadcastFavoritesChanged(const FContentBrowserItemPath& ChangedItem, bool bAdded) const
{
OnFavoritesChanged.Broadcast(ChangedItem, bAdded);
}
void FContentBrowserSingleton::PopulateConfigValues()
{
const FString ContentBrowserSection = TEXT("ContentBrowser");
// PluginSettings
{
const FString PluginSettingsName = TEXT("PluginSettings");
TArray<FString> PluginSettingsStrings;
GConfig->GetArray(*ContentBrowserSection, *PluginSettingsName, PluginSettingsStrings, GEditorIni);
for (const FString& PluginSettingString : PluginSettingsStrings)
{
FContentBrowserPluginSettings PluginSetting;
const TCHAR* Result = FContentBrowserPluginSettings::StaticStruct()->ImportText(*PluginSettingString, &PluginSetting, nullptr, PPF_None, GLog, PluginSettingsName);
if (Result != nullptr)
{
PluginSettings.Emplace(PluginSetting);
}
}
}
}
void FContentBrowserSingleton::GetContentBrowserSubMenu(UToolMenu* Menu, TSharedRef<FWorkspaceItem> ContentBrowserGroup)
{
// Register the tab spawners for all content browsers
FToolMenuSection& Section = Menu->AddSection("ContentBrowser");
Section.AddMenuEntry("FocusContentBrowser",
LOCTEXT("FocusContentBrowser_Label", "Focus Content Browser"),
LOCTEXT("FocusContentBrowser_Desc", "Focuses the most recently active content browser tab."),
FSlateIcon(),
FUIAction(FExecuteAction::CreateRaw(this, &FContentBrowserSingleton::FocusPrimaryContentBrowser, true), FCanExecuteAction())
);
auto AddSpawnerEntryToMenuSection = [](FToolMenuSection& InSection, TSharedPtr<FTabSpawnerEntry> InSpawnerNode, FName InTabID)
{
InSection.AddMenuEntry(
InTabID,
InSpawnerNode->GetDisplayName().IsEmpty() ? FText::FromName(InTabID) : InSpawnerNode->GetDisplayName(),
InSpawnerNode->GetTooltipText(),
FSlateIcon(),
FGlobalTabmanager::Get()->GetUIActionForTabSpawnerMenuEntry(InSpawnerNode),
EUserInterfaceActionType::Check
);
};
for (int32 BrowserIdx = 0; BrowserIdx < UE_ARRAY_COUNT(ContentBrowserTabIDs); BrowserIdx++)
{
const FName TabID = ContentBrowserTabIDs[BrowserIdx];
AddSpawnerEntryToMenuSection(Section, ContentBrowserTabs[BrowserIdx], TabID);
}
}
FContentBrowserItemPath FContentBrowserSingleton::GetInitialPathToSaveAsset(const FContentBrowserItemPath& InPath)
{
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
bool bPathIsWritable = true;
if (InPath.GetVirtualPathName().IsNone() || !InPath.HasInternalPath())
{
bPathIsWritable = false;
}
else
{
bPathIsWritable = AssetToolsModule.Get().GetWritableFolderPermissionList()->PassesStartsWithFilter(InPath.GetInternalPathName(), /*bAllowParentPaths*/true);
}
FString AssetPath;
if (bPathIsWritable)
{
AssetPath = InPath.GetInternalPathString();
}
else
{
// Try last path
const FString DefaultFilesystemDirectory = FEditorDirectories::Get().GetLastDirectory(ELastDirectory::NEW_ASSET);
if (!DefaultFilesystemDirectory.IsEmpty() && FPackageName::TryConvertFilenameToLongPackageName(DefaultFilesystemDirectory, AssetPath))
{
if (AssetToolsModule.Get().GetWritableFolderPermissionList()->PassesStartsWithFilter(AssetPath, /*bAllowParentPaths*/true))
{
bPathIsWritable = true;
}
}
if (!bPathIsWritable)
{
AssetPath.Reset();
// Request default virtual paths, use first path that passes
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().GetModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
TArray<FName> VirtualPaths;
if (ContentBrowserModule.GetDefaultSelectedPathsDelegate().ExecuteIfBound(VirtualPaths) && VirtualPaths.Num() > 0)
{
for (const FName VirtualPath : VirtualPaths)
{
if (IContentBrowserDataModule::Get().GetSubsystem()->TryConvertVirtualPath(FNameBuilder(VirtualPath), AssetPath) == EContentBrowserPathType::Internal)
{
if (AssetToolsModule.Get().GetWritableFolderPermissionList()->PassesStartsWithFilter(AssetPath, /*bAllowParentPaths*/true))
{
break;
}
}
AssetPath.Reset();
}
}
}
if (AssetPath.IsEmpty())
{
// No saved path, just use the game content root
AssetPath = TEXT("/Game");
}
}
// Remove trailing slash
if (AssetPath.EndsWith(TEXT("/")) || AssetPath.EndsWith(TEXT("\\")))
{
AssetPath.LeftChopInline(1, EAllowShrinking::No);
}
return FContentBrowserItemPath(AssetPath, EContentBrowserPathType::Internal);
}
TArray<FString> FContentBrowserSingleton::GetAliasesForPath(const FSoftObjectPath& InPath) const
{
TArray<FString> OutPaths;
const TArray<FContentBrowserItemPath> Items = IContentBrowserDataModule::Get().GetSubsystem()->GetAliasesForPath(InPath);
for (const FContentBrowserItemPath& Item : Items)
{
OutPaths.Add(Item.GetInternalPathString());
}
return OutPaths;
}
void FContentBrowserSingleton::AddCustomTextFilter(const FCustomTextFilterData& FilterData, bool bApplyFilter)
{
if (!HasPrimaryContentBrowser())
{
ChooseNewPrimaryBrowser();
}
if (PrimaryContentBrowser.IsValid())
{
PrimaryContentBrowser.Pin()->AddCustomTextFilter(FilterData, bApplyFilter);
}
}
void FContentBrowserSingleton::DeleteCustomTextFilterByLabel(const FText& FilterLabel)
{
if (!HasPrimaryContentBrowser())
{
ChooseNewPrimaryBrowser();
}
if (PrimaryContentBrowser.IsValid())
{
PrimaryContentBrowser.Pin()->DeleteCustomTextFilterByLabel(FilterLabel);
}
}
void FContentBrowserSingleton::ModifyCustomTextFilterByLabel(const FCustomTextFilterData& NewFilterData, const FText& FilterLabel)
{
if (!HasPrimaryContentBrowser())
{
ChooseNewPrimaryBrowser();
}
if (PrimaryContentBrowser.IsValid())
{
PrimaryContentBrowser.Pin()->ModifyCustomTextFilterByLabel(NewFilterData, FilterLabel);
}
}
/**
* Checks if the Primary Content Browser's AssetView finished filtering
*
* @return true if the AssetView is done filtering
*/
bool FContentBrowserSingleton::IsAssetViewDoneFiltering()
{
bool isDoneFiltering = false;
if (!HasPrimaryContentBrowser())
{
ChooseNewPrimaryBrowser();
}
if (PrimaryContentBrowser.IsValid())
{
isDoneFiltering = PrimaryContentBrowser.Pin()->IsAssetViewDoneFiltering();
}
return isDoneFiltering;
}
/**
* Sets the search text for the Primary Content Browser
*
* @params NewText - the text to put in the search box
*/
void FContentBrowserSingleton::SetSearchText(const FText& NewText)
{
if (!HasPrimaryContentBrowser())
{
ChooseNewPrimaryBrowser();
}
if (PrimaryContentBrowser.IsValid())
{
if (TSharedPtr<SContentBrowser> PrimaryContentBrowserPinned = PrimaryContentBrowser.Pin())
{
PrimaryContentBrowserPinned->SetSearchText(NewText);
PrimaryContentBrowserPinned->OnSearchBoxCommitted(NewText, ETextCommit::Type::Default);
}
}
}
void FContentBrowserSingleton::ToggleFolderFavorite(const TArray<FString>& FolderPaths)
{
if (!HasPrimaryContentBrowser())
{
ChooseNewPrimaryBrowser();
}
if (PrimaryContentBrowser.IsValid())
{
PrimaryContentBrowser.Pin()->ToggleFolderFavorite(FolderPaths);
}
}
void FContentBrowserSingleton::ForEachFavoriteFolder(TFunctionRef<void(const FContentBrowserItemPath&)> InDelegate)
{
for (const FString& Path : FavoriteFolderPaths)
{
InDelegate(FContentBrowserItemPath(Path, EContentBrowserPathType::Internal));
}
}
void FContentBrowserSingleton::SetFilterLayout(const EFilterBarLayout InFilterBarLayout)
{
if (!HasPrimaryContentBrowser())
{
ChooseNewPrimaryBrowser();
}
if (PrimaryContentBrowser.IsValid())
{
PrimaryContentBrowser.Pin()->SetFilterLayout(InFilterBarLayout);
}
}
EFilterBarLayout FContentBrowserSingleton::GetFilterLayout()
{
EFilterBarLayout FilterBarLayout = EFilterBarLayout::Vertical;
if (!HasPrimaryContentBrowser())
{
ChooseNewPrimaryBrowser();
}
if (PrimaryContentBrowser.IsValid())
{
FilterBarLayout = PrimaryContentBrowser.Pin()->GetFilterLayout();
}
return FilterBarLayout;
}
TSharedPtr<SWidget> FContentBrowserSingleton::GetActiveFilterContainer()
{
if (!HasPrimaryContentBrowser())
{
ChooseNewPrimaryBrowser();
}
if (PrimaryContentBrowser.IsValid())
{
return PrimaryContentBrowser.Pin()->GetActiveFilterContainer();
}
return nullptr;
}
void FContentBrowserSingleton::RegisterContentSourceFactory(const FName& InContentSourceName, const FContentSourceFactory& InContentSourceFactory)
{
ContentSourceFactories.Emplace(InContentSourceName, InContentSourceFactory);
ContentSourceFactoriesChangedEvent.Broadcast();
}
void FContentBrowserSingleton::UnregisterContentSourceFactory(const FName& InContentSourceName)
{
ContentSourceFactories.Remove(InContentSourceName);
ContentSourceFactoriesChangedEvent.Broadcast();
}
void FContentBrowserSingleton::ForEachContentSourceFactory(const TFunctionRef<void(const FName&, const FContentSourceFactory&)>& InFunction)
{
for(const TPair<FName, FContentSourceFactory>& Pair : ContentSourceFactories)
{
InFunction(Pair.Key, Pair.Value);
}
}
IContentBrowserSingleton::FOnContentSourceFactoriesChanged& FContentBrowserSingleton::OnContentSourceFactoriesChanged()
{
return ContentSourceFactoriesChangedEvent;
}
#undef LOCTEXT_NAMESPACE