// 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("ContentBrowser"); return ContentBrowserModule.Get(); } FContentBrowserSingleton::FContentBrowserSingleton() : CollectionAssetRegistryBridge(MakeShared()) , SettingsStringID(0) { const FSlateIcon ContentBrowserIcon(UE::ContentBrowser::Private::FContentBrowserStyle::Get().GetStyleSetName(), "ContentBrowser.TabIcon"); const IWorkspaceMenuStructure& MenuStructure = WorkspaceMenu::GetMenuStructure(); TSharedRef 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(); ShowPrivateContentState.InvariantPaths->OnFilterChanged().AddRaw(this, &FContentBrowserSingleton::SetPrivateContentPermissionListDirty); ShowPrivateContentState.CachedVirtualPaths = MakeShared(); ShowEpicInternalContentState.InvariantPaths = MakeShared(); } 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 FContentBrowserSingleton::CreateAssetPicker(const FAssetPickerConfig& AssetPickerConfig) { return SNew( SAssetPicker ) .IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() ) .AssetPickerConfig(AssetPickerConfig); } TSharedPtr FContentBrowserSingleton::GetAssetPickerSearchBox(const TSharedRef& AssetPickerWidget) { TSharedRef AssetPicker = StaticCastSharedRef(AssetPickerWidget); return AssetPicker->GetSearchBox(); } TSharedRef FContentBrowserSingleton::CreatePathPicker(const FPathPickerConfig& PathPickerConfig) { return SNew( SPathPicker ) .IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() ) .PathPickerConfig(PathPickerConfig); } TSharedRef FContentBrowserSingleton::CreateCollectionPicker(const FCollectionPickerConfig& CollectionPickerConfig) { return SNew( SCollectionPicker ) .IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() ) .CollectionPickerConfig(CollectionPickerConfig); } TSharedRef FContentBrowserSingleton::CreateContentBrowserDrawer(const FContentBrowserConfig& ContentBrowserConfig, TFunction()> InOnGetTabForDrawer) { TSharedPtr 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 AssetDialog = SNew(SAssetDialog, InConfig); AssetDialog->SetOnAssetsChosenForOpen(InOnAssetsChosenForOpen); AssetDialog->SetOnAssetDialogCancelled(InOnAssetDialogCancelled); SharedCreateAssetDialogWindow(AssetDialog, InConfig, bModal); } TArray FContentBrowserSingleton::CreateModalOpenAssetDialog(const FOpenAssetDialogConfig& InConfig) { struct FModalResults { void OnAssetsChosenForOpen(const TArray& SelectedAssets) { SavedResults = SelectedAssets; } TArray SavedResults; }; FModalResults ModalWindowResults; FOnAssetsChosenForOpen OnAssetsChosenForOpenDelegate = FOnAssetsChosenForOpen::CreateRaw(&ModalWindowResults, &FModalResults::OnAssetsChosenForOpen); const bool bModal = true; TSharedRef 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 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 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 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 ContentBrowserWidget) { TSharedPtr BrowserToFocus; if (ContentBrowserWidget.IsValid() ) { for (TWeakPtr& 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 ContentBrowserToSync = FindContentBrowserToSync(bAllowLockedBrowsers, ContentBrowserInstanceName, bCreateNewContentBrowser); if (!ContentBrowserToSync.IsValid()) { FocusPrimaryContentBrowser(false); ContentBrowserToSync = PrimaryContentBrowser.Pin(); } if (ContentBrowserToSync.IsValid()) { ContentBrowserToSync->CreateNewAsset(DefaultAssetName, PackagePath, AssetClass, Factory); } } TSharedPtr FContentBrowserSingleton::FindContentBrowserToSync(bool bAllowLockedBrowsers, const FName& InstanceName, bool bNewSpawnBrowser) { TSharedPtr 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()->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& AssetDataList, bool bAllowLockedBrowsers, bool bFocusContentBrowser, const FName& InstanceName, bool bNewSpawnBrowser) { TSharedPtr ContentBrowserToSync = FindContentBrowserToSync(bAllowLockedBrowsers, InstanceName, bNewSpawnBrowser); if ( ContentBrowserToSync.IsValid() ) { // Finally, focus and sync the browser that was found if (bFocusContentBrowser) { FocusContentBrowser(ContentBrowserToSync); } TSet OuterPathNames; TArray 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 PackagesToAssetDataMap; UE::AssetRegistry::GetAssetForPackages(OuterPathNames.Array(), PackagesToAssetDataMap); Algo::Transform(PackagesToAssetDataMap, AssetDataListToSync, [](const TPair& Pair) { return Pair.Value; }); } ContentBrowserToSync->SyncToAssets(AssetDataListToSync); } } void FContentBrowserSingleton::SyncBrowserToAssets(const TArray& AssetList, bool bAllowLockedBrowsers, bool bFocusContentBrowser, const FName& InstanceName, bool bNewSpawnBrowser) { // Convert UObject* array to FAssetData array TArray 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& FolderList, bool bAllowLockedBrowsers, bool bFocusContentBrowser, const FName& InstanceName, bool bNewSpawnBrowser) { TSharedPtr 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& ItemsToSync, bool bAllowLockedBrowsers, bool bFocusContentBrowser, const FName& InstanceName, bool bNewSpawnBrowser) { TSharedPtr 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 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& SelectedAssets) { if ( PrimaryContentBrowser.IsValid() ) { PrimaryContentBrowser.Pin()->GetSelectedAssets(SelectedAssets); } } void FContentBrowserSingleton::GetSelectedFolders(TArray& SelectedFolders) { if (PrimaryContentBrowser.IsValid()) { PrimaryContentBrowser.Pin()->GetSelectedFolders(SelectedFolders); } } void FContentBrowserSingleton::GetSelectedPathViewFolders(TArray& 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& 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(ModuleName); return static_cast(Module.Get()); } void FContentBrowserSingleton::SetPrimaryContentBrowser(const TSharedRef& 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& 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& 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 DialogWindow = SNew(SWindow) .Title(WindowTitle) .ClientSize(WindowSize); DialogWindow->SetContent(AssetDialog); TSharedPtr ParentWindow = InConfig.WindowOverride; if (!InConfig.WindowOverride) { IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked(TEXT("MainFrame")); const TSharedPtr& 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 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& BrowserToFocus) { if ( BrowserToFocus.IsValid() ) { if (BrowserToFocus == ContentBrowserDrawer) { GEditor->GetEditorSubsystem()->OpenContentBrowserDrawer(); } else { TSharedRef Browser = BrowserToFocus.ToSharedRef(); TSharedPtr TabManager = Browser->GetTabManager(); if (TabManager.IsValid()) { TabManager->TryInvokeTab(Browser->GetInstanceName()); } } } } void FContentBrowserSingleton::DockContentBrowserDrawer() { TSharedPtr ForTabManager; TSharedPtr 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 ExistingTab = ForTabManager->FindExistingLiveTab(ContentBrowserTabIDs[BrowserIdx])) { GEditor->GetEditorSubsystem()->ForceDismissDrawer(); ExistingTab->ActivateInParent(ETabActivationCause::SetDirectly); bExistingTab = true; break; } } } if(!bExistingTab) { TSharedPtr 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 Browser = AllContentBrowsers[AllBrowsersIdx].Pin()) { if (Browser->GetInstanceName() == InstanceName) { Browser->CopySettingsFromBrowser(ContentBrowserDrawerPinned); break; } } } GEditor->GetEditorSubsystem()->ForceDismissDrawer(); } } FName FContentBrowserSingleton::SummonNewBrowser(bool bAllowLockedBrowsers, TSharedPtr SpecificTabManager) { TSet OpenBrowserIDs; // Find all currently open browsers to help find the first open slot for (int32 BrowserIdx = AllContentBrowsers.Num() - 1; BrowserIdx >= 0; --BrowserIdx) { const TWeakPtr& 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& 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 FContentBrowserSingleton::CreateContentBrowser( const FName InstanceName, TSharedPtr ContainingTab, const FContentBrowserConfig* ContentBrowserConfig ) { TSharedRef 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 FContentBrowserSingleton::SpawnContentBrowserTab( const FSpawnTabArgs& SpawnTabArgs, int32 BrowserIdx ) { TAttribute Label = TAttribute::Create( TAttribute::FGetter::CreateRaw( this, &FContentBrowserSingleton::GetContentBrowserTabLabel, BrowserIdx ) ); TSharedRef NewTab = SNew(SDockTab) .TabRole(ETabRole::NomadTab) .Label( Label ) .ToolTip( IDocumentation::Get()->CreateToolTip( Label, nullptr, "Shared/ContentBrowser", "Tab" ) ) .OnExtendContextMenu_Raw(this, &FContentBrowserSingleton::ExtendContentBrowserTabContextMenu); TSharedRef NewBrowser = CreateContentBrowser( SpawnTabArgs.GetTabId().TabType, NewTab, nullptr ); // Add wrapper for tutorial highlighting TSharedRef Wrapper = SNew(SBox) .AddMetaData(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& 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& 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& 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& 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& 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 ContentBrowserWidget) { TSharedPtr BrowserToSave; if (ContentBrowserWidget.IsValid()) { for (TWeakPtr& 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 PickerWidget) { if (PickerWidget.IsValid()) { if (PickerWidget->GetType() == FName(TEXT("SAssetPicker"))) { TSharedPtr AssetPicker = StaticCastSharedPtr(PickerWidget); AssetPicker->ExecuteRenameCommand(); } else if (PickerWidget->GetType() == FName(TEXT("SPathPicker"))) { TSharedPtr PathPicker = StaticCastSharedPtr(PickerWidget); PathPicker->ExecuteRenameFolder(); } } } void FContentBrowserSingleton::ExecuteAddFolder(TSharedPtr Widget) { if (Widget.IsValid()) { if (Widget->GetType() == FName(TEXT("SPathPicker"))) { TSharedPtr PathPicker = StaticCastSharedPtr(Widget); PathPicker->ExecuteAddFolder(); } } } void FContentBrowserSingleton::RefreshPathView(TSharedPtr Widget) { if (Widget.IsValid()) { if (Widget->GetType() == FName(TEXT("SPathPicker"))) { TSharedPtr PathPicker = StaticCastSharedPtr(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(); TArray 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& FContentBrowserSingleton::GetShowPrivateContentPermissionList() { return ShowPrivateContentState.InvariantPaths; } const TSharedPtr& 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 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 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 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("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(TEXT("ContentBrowser")); TArray 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 FContentBrowserSingleton::GetAliasesForPath(const FSoftObjectPath& InPath) const { TArray OutPaths; const TArray 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 PrimaryContentBrowserPinned = PrimaryContentBrowser.Pin()) { PrimaryContentBrowserPinned->SetSearchText(NewText); PrimaryContentBrowserPinned->OnSearchBoxCommitted(NewText, ETextCommit::Type::Default); } } } void FContentBrowserSingleton::ToggleFolderFavorite(const TArray& FolderPaths) { if (!HasPrimaryContentBrowser()) { ChooseNewPrimaryBrowser(); } if (PrimaryContentBrowser.IsValid()) { PrimaryContentBrowser.Pin()->ToggleFolderFavorite(FolderPaths); } } void FContentBrowserSingleton::ForEachFavoriteFolder(TFunctionRef 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 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& InFunction) { for(const TPair& Pair : ContentSourceFactories) { InFunction(Pair.Key, Pair.Value); } } IContentBrowserSingleton::FOnContentSourceFactoriesChanged& FContentBrowserSingleton::OnContentSourceFactoriesChanged() { return ContentSourceFactoriesChangedEvent; } #undef LOCTEXT_NAMESPACE