Files
UnrealEngineUWP/Engine/Source/Editor/ContentBrowser/Private/SContentBrowser.cpp

1987 lines
63 KiB
C++
Raw Normal View History

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "ContentBrowserPCH.h"
#include "AssetContextMenu.h"
#include "NewAssetContextMenu.h"
#include "PathContextMenu.h"
#include "ContentBrowserModule.h"
#include "ContentBrowserCommands.h"
#include "CollectionManagerModule.h"
#include "AssetRegistryModule.h"
#define LOCTEXT_NAMESPACE "ContentBrowser"
const FString SContentBrowser::SettingsIniSection = TEXT("ContentBrowser");
SContentBrowser::~SContentBrowser()
{
// Remove the listener for when view settings are changed
UContentBrowserSettings::OnSettingChanged().RemoveAll( this );
// Remove listeners for when collections/paths are renamed/deleted
FCollectionManagerModule* CollectionManagerModule = FModuleManager::GetModulePtr<FCollectionManagerModule>(TEXT("CollectionManager"));
if (CollectionManagerModule != nullptr)
{
CollectionManagerModule->Get().OnCollectionRenamed().RemoveAll(this);
CollectionManagerModule->Get().OnCollectionDestroyed().RemoveAll(this);
}
FAssetRegistryModule* AssetRegistryModule = FModuleManager::GetModulePtr<FAssetRegistryModule>(TEXT("AssetRegistry"));
if (AssetRegistryModule != nullptr)
{
AssetRegistryModule->Get().OnPathRemoved().RemoveAll(this);
}
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SContentBrowser::Construct( const FArguments& InArgs, const FName& InInstanceName )
{
if ( InArgs._ContainingTab.IsValid() )
{
// For content browsers that are placed in tabs, save settings when the tab is closing.
ContainingTab = InArgs._ContainingTab;
InArgs._ContainingTab->SetOnPersistVisualState( SDockTab::FOnPersistVisualState::CreateSP( this, &SContentBrowser::OnContainingTabSavingVisualState ) );
InArgs._ContainingTab->SetOnTabClosed( SDockTab::FOnTabClosedCallback::CreateSP( this, &SContentBrowser::OnContainingTabClosed ) );
InArgs._ContainingTab->SetOnTabActivated( SDockTab::FOnTabActivatedCallback::CreateSP( this, &SContentBrowser::OnContainingTabActivated ) );
}
bIsLocked = InArgs._InitiallyLocked;
HistoryManager.SetOnApplyHistoryData(FOnApplyHistoryData::CreateSP(this, &SContentBrowser::OnApplyHistoryData));
HistoryManager.SetOnUpdateHistoryData(FOnUpdateHistoryData::CreateSP(this, &SContentBrowser::OnUpdateHistoryData));
PathContextMenu = MakeShareable(new FPathContextMenu( AsShared() ));
PathContextMenu->SetOnNewAssetRequested( FNewAssetContextMenu::FOnNewAssetRequested::CreateSP(this, &SContentBrowser::NewAssetRequested) );
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
PathContextMenu->SetOnRenameFolderRequested(FPathContextMenu::FOnRenameFolderRequested::CreateSP(this, &SContentBrowser::OnRenameFolderRequested));
PathContextMenu->SetOnFolderDeleted(FPathContextMenu::FOnFolderDeleted::CreateSP(this, &SContentBrowser::OnOpenedFolderDeleted));
FrontendFilters = MakeShareable(new AssetFilterCollectionType());
TextFilter = MakeShareable( new FFrontendFilter_Text() );
FContentBrowserCommands::Register();
BindCommands();
ChildSlot
[
SNew(SVerticalBox)
// Path and history
+SVerticalBox::Slot()
.AutoHeight()
.Padding( 0, 0, 0, 0 )
[
SNew( SWrapBox )
.UseAllottedWidth( true )
.InnerSlotPadding( FVector2D( 5, 2 ) )
+ SWrapBox::Slot()
.FillLineWhenWidthLessThan( 600 )
.FillEmptySpace( true )
[
SNew( SHorizontalBox )
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew( SBorder )
.Padding( FMargin( 3 ) )
.BorderImage( FEditorStyle::GetBrush( "ToolPanel.GroupBorder" ) )
[
SNew( SHorizontalBox )
// New
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign( VAlign_Center )
.HAlign( HAlign_Left )
.Padding(0,0,4,0)
[
SNew( SComboButton )
.ComboButtonStyle( FEditorStyle::Get(), "ContentBrowser.NewAsset.Style" )
.ForegroundColor(FLinearColor::White)
.ContentPadding(0)
.OnGetMenuContent( this, &SContentBrowser::MakeCreateAssetContextMenu )
.ToolTipText( this, &SContentBrowser::GetNewAssetToolTipText )
.IsEnabled( this, &SContentBrowser::IsAssetPathSelected )
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserNewAsset")))
.ButtonContent()
[
SNew( SHorizontalBox )
// New Icon
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign( VAlign_Center )
[
SNew( SImage )
.Image( FEditorStyle::GetBrush( "ContentBrowser.NewAsset" ) )
]
// New Text
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(0,0,2,0)
[
SNew( STextBlock )
.TextStyle( FEditorStyle::Get(), "ContentBrowser.TopBar.Font" )
.Text( LOCTEXT( "NewButton", "Create" ) )
]
]
]
// Import
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign( VAlign_Center )
.HAlign( HAlign_Left )
.Padding(0,0,10,0)
[
SNew( SButton )
.ButtonStyle( FEditorStyle::Get(), "ToggleButton" )
.ToolTipText( this, &SContentBrowser::GetImportTooltipText )
.IsEnabled( this, &SContentBrowser::IsAssetPathSelected )
.OnClicked( this, &SContentBrowser::HandleImportClicked )
.ContentPadding( 0 )
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserImportAsset")))
[
SNew( SHorizontalBox )
// Import Icon
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign( VAlign_Center )
[
SNew( SImage )
.Image( FEditorStyle::GetBrush( "ContentBrowser.ImportPackage" ) )
]
// Import Text
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(0,0,2,0)
[
SNew( STextBlock )
.TextStyle( FEditorStyle::Get(), "ContentBrowser.TopBar.Font" )
.Text( LOCTEXT( "Import", "Import" ) )
]
]
]
// Save
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.VAlign(VAlign_Center)
.HAlign(HAlign_Left)
[
SNew( SButton )
.ButtonStyle( FEditorStyle::Get(), "ToggleButton" )
.ToolTipText( LOCTEXT( "SaveDirtyPackagesTooltip", "Save all modified assets." ) )
.ContentPadding( 0 )
.OnClicked( this, &SContentBrowser::OnSaveClicked )
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserSaveDirtyPackages")))
[
SNew( SHorizontalBox )
// Save All Icon
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign( VAlign_Center )
[
SNew( SImage )
.Image( FEditorStyle::GetBrush( "ContentBrowser.SaveDirtyPackages" ) )
]
// Save All Text
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(0,0,2,0)
[
SNew( STextBlock )
.TextStyle( FEditorStyle::Get(), "ContentBrowser.TopBar.Font" )
.Text( LOCTEXT( "SaveAll", "Save All" ) )
]
]
]
]
]
]
+ SWrapBox::Slot()
.FillEmptySpace( true )
[
SNew(SBorder)
.Padding(FMargin(3))
.BorderImage( FEditorStyle::GetBrush("ToolPanel.GroupBorder") )
[
SNew(SHorizontalBox)
// History Back Button
+SHorizontalBox::Slot()
.AutoWidth()
//.Padding(0, 1)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SButton)
.VAlign(EVerticalAlignment::VAlign_Center)
.ButtonStyle( FEditorStyle::Get(), "ToggleButton" )
.ForegroundColor( FEditorStyle::GetSlateColor("DefaultForeground") )
.ToolTipText( this, &SContentBrowser::GetHistoryBackTooltip )
.ContentPadding( FMargin(1, 0) )
.OnClicked(this, &SContentBrowser::BackClicked)
.IsEnabled(this, &SContentBrowser::IsBackEnabled)
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserHistoryBack")))
[
SNew(SImage)
.Image(FEditorStyle::GetBrush("ContentBrowser.HistoryBack"))
]
]
]
// History Forward Button
+ SHorizontalBox::Slot()
.AutoWidth()
//.Padding(0, 1)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SButton)
.VAlign(EVerticalAlignment::VAlign_Center)
.ButtonStyle( FEditorStyle::Get(), "ToggleButton" )
.ForegroundColor( FEditorStyle::GetSlateColor("DefaultForeground") )
.ToolTipText( this, &SContentBrowser::GetHistoryForwardTooltip )
.ContentPadding( FMargin(1, 0) )
.OnClicked(this, &SContentBrowser::ForwardClicked)
.IsEnabled(this, &SContentBrowser::IsForwardEnabled)
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserHistoryForward")))
[
SNew(SImage)
.Image(FEditorStyle::GetBrush("ContentBrowser.HistoryForward"))
]
]
]
// Separator
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(3, 0)
[
SNew(SSeparator)
.Orientation(Orient_Vertical)
]
// Path picker
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign( VAlign_Fill )
[
SAssignNew( PathPickerButton, SComboButton )
.ComboButtonStyle( FEditorStyle::Get(), "ToolbarComboButton" )
.ForegroundColor(FLinearColor::White)
.ToolTipText( LOCTEXT( "PathPickerTooltip", "Choose a path" ) )
.OnGetMenuContent( this, &SContentBrowser::GetPathPickerContent )
.HasDownArrow( false )
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserPathPicker")))
.ButtonContent()
[
SNew( SImage )
.Image( FEditorStyle::GetBrush( "ContentBrowser.Sources" ) )
]
]
// Path
+ SHorizontalBox::Slot()
.VAlign(VAlign_Fill)
.FillWidth(1.0f)
.Padding( FMargin(0) )
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.FillWidth(1.0f)
[
SAssignNew(PathBreadcrumbTrail, SBreadcrumbTrail<FString>)
//.ToolTipText( LOCTEXT("PathTooltip", "Content Path") )
.ButtonContentPadding(FMargin(3.0f, 3.0f))
.DelimiterImage(FEditorStyle::GetBrush("ContentBrowser.PathDelimiter"))
.TextStyle(FEditorStyle::Get(), "ContentBrowser.PathText")
.ShowLeadingDelimiter( false )
.InvertTextColorOnHover( false )
.OnCrumbClicked(this, &SContentBrowser::OnPathClicked)
.GetCrumbMenuContent(this, &SContentBrowser::OnGetCrumbDelimiterContent)
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserPath")))
]
]
// Lock button
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SButton)
.VAlign(EVerticalAlignment::VAlign_Center)
.ButtonStyle( FEditorStyle::Get(), "NoBorder" )
.ToolTipText( LOCTEXT("LockToggleTooltip", "Toggle lock. If locked, this browser will ignore Find in Content Browser requests.") )
.ContentPadding( FMargin(1, 0) )
.OnClicked(this, &SContentBrowser::ToggleLockClicked)
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserLock")))
[
SNew(SImage)
.Image( this, &SContentBrowser::GetToggleLockImage)
]
]
]
]
]
]
// Assets/tree
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
// The tree/assets splitter
SAssignNew(PathAssetSplitterPtr, SSplitter)
// Sources View
+ SSplitter::Slot()
.Value(0.3f)
[
SNew(SVerticalBox)
.Visibility( this, &SContentBrowser::GetSourcesViewVisibility )
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SAssignNew(PathCollectionSplitterPtr, SSplitter)
.Style( FEditorStyle::Get(), "ContentBrowser.Splitter" )
.Orientation( Orient_Vertical )
// Path View
+ SSplitter::Slot()
.Value(0.9f)
[
SNew(SBorder)
.Padding(FMargin(3))
.BorderImage( FEditorStyle::GetBrush("ToolPanel.GroupBorder") )
[
SAssignNew( PathViewPtr, SPathView )
.OnPathSelected( this, &SContentBrowser::PathSelected )
.OnGetFolderContextMenu( this, &SContentBrowser::GetFolderContextMenu, true )
.OnGetPathContextMenuExtender( this, &SContentBrowser::GetPathContextMenuExtender )
.FocusSearchBoxWhenOpened( false )
.ShowTreeTitle( false )
.ShowSeparator( false )
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserSources")))
.SearchContent()
[
SNew( SVerticalBox )
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserSourcesToggle")))
+ SVerticalBox::Slot()
.FillHeight( 1.0f )
.Padding(0,0,2,0)
[
SNew( SButton )
.VAlign( EVerticalAlignment::VAlign_Center )
.ButtonStyle( FEditorStyle::Get(), "ToggleButton" )
.ToolTipText( LOCTEXT( "SourcesTreeToggleTooltip", "Show or hide the sources panel" ) )
.ContentPadding( FMargin( 1, 0 ) )
.ForegroundColor( FEditorStyle::GetSlateColor( "DefaultForeground" ) )
.OnClicked( this, &SContentBrowser::SourcesViewExpandClicked )
[
SNew( SImage )
.Image( this, &SContentBrowser::GetSourcesToggleImage )
]
]
]
]
]
// Collection View
+ SSplitter::Slot()
.Value(0.1f)
[
SNew(SBorder)
.Padding(FMargin(3))
.BorderImage( FEditorStyle::GetBrush("ToolPanel.GroupBorder") )
[
SAssignNew(CollectionViewPtr, SCollectionView)
.OnCollectionSelected(this, &SContentBrowser::CollectionSelected)
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserCollections")))
]
]
]
]
// Asset View
+ SSplitter::Slot()
.Value(0.7f)
[
SNew(SBorder)
.Padding(FMargin(3))
.BorderImage( FEditorStyle::GetBrush("ToolPanel.GroupBorder") )
[
SNew(SVerticalBox)
// Search and commands
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
// Expand/collapse sources button
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding( 0, 0, 4, 0 )
[
SNew( SVerticalBox )
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserSourcesToggle")))
+ SVerticalBox::Slot()
.FillHeight( 1.0f )
[
SNew( SButton )
.VAlign( EVerticalAlignment::VAlign_Center )
.ButtonStyle( FEditorStyle::Get(), "ToggleButton" )
.ToolTipText( LOCTEXT( "SourcesTreeToggleTooltip", "Show or hide the sources panel" ) )
.ContentPadding( FMargin( 1, 0 ) )
.ForegroundColor( FEditorStyle::GetSlateColor( "DefaultForeground" ) )
.OnClicked( this, &SContentBrowser::SourcesViewExpandClicked )
.Visibility( this, &SContentBrowser::GetPathExpanderVisibility )
[
SNew( SImage )
.Image( this, &SContentBrowser::GetSourcesToggleImage )
]
]
]
// Filter
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew( SComboButton )
.ComboButtonStyle( FEditorStyle::Get(), "ContentBrowser.Filters.Style" )
.ForegroundColor(FLinearColor::White)
.ContentPadding(0)
.ToolTipText( LOCTEXT( "AddFilterToolTip", "Add an asset filter." ) )
.OnGetMenuContent( this, &SContentBrowser::MakeAddFilterMenu )
.HasDownArrow( true )
.ContentPadding( FMargin( 1, 0 ) )
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserFiltersCombo")))
.ButtonContent()
[
SNew( STextBlock )
.TextStyle( FEditorStyle::Get(), "ContentBrowser.Filters.Text" )
.Text( LOCTEXT( "Filters", "Filters" ) )
]
]
// Search
+SHorizontalBox::Slot()
.Padding(4, 0, 0, 0)
.VAlign(VAlign_Center)
.FillWidth(1.0f)
[
SAssignNew(SearchBoxPtr, SAssetSearchBox)
.HintText( this, &SContentBrowser::GetSearchAssetsHintText )
.OnTextChanged( this, &SContentBrowser::OnSearchBoxChanged )
.OnTextCommitted( this, &SContentBrowser::OnSearchBoxCommitted )
.PossibleSuggestions( this, &SContentBrowser::GetAssetSearchSuggestions )
.DelayChangeNotificationsWhileTyping( true )
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserSearchAssets")))
]
]
// Filters
+ SVerticalBox::Slot()
.AutoHeight()
[
SAssignNew(FilterListPtr, SFilterList)
.OnFilterChanged(this, &SContentBrowser::OnFilterChanged)
.OnGetContextMenu(this, &SContentBrowser::GetFilterContextMenu)
.FrontendFilters(FrontendFilters)
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserFilters")))
]
// Assets
+ SVerticalBox::Slot()
.FillHeight( 1.0f )
.Padding( 0 )
[
SAssignNew(AssetViewPtr, SAssetView)
.ThumbnailScale( 0.0f )
.OnPathSelected(this, &SContentBrowser::FolderEntered)
.OnAssetSelected(this, &SContentBrowser::OnAssetSelectionChanged)
.OnAssetsActivated(this, &SContentBrowser::OnAssetsActivated)
.OnGetAssetContextMenu(this, &SContentBrowser::OnGetAssetContextMenu)
.OnGetFolderContextMenu(this, &SContentBrowser::GetFolderContextMenu, false)
.OnGetPathContextMenuExtender(this, &SContentBrowser::GetPathContextMenuExtender)
.OnFindInAssetTreeRequested(this, &SContentBrowser::OnFindInAssetTreeRequested)
.OnAssetRenameCommitted(this, &SContentBrowser::OnAssetRenameCommitted)
.AreRealTimeThumbnailsAllowed(this, &SContentBrowser::IsHovered)
.FrontendFilters(FrontendFilters)
.HighlightedText(this, &SContentBrowser::GetHighlightedText)
.AllowThumbnailEditMode(true)
.AllowThumbnailHintLabel(false)
.CanShowFolders(true)
.CanShowRealTimeThumbnails(true)
.CanShowDevelopersFolder(true)
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("ContentBrowserAssets")))
]
]
]
]
];
AssetContextMenu = MakeShareable(new FAssetContextMenu(AssetViewPtr));
AssetContextMenu->BindCommands(Commands);
AssetContextMenu->SetOnFindInAssetTreeRequested( FOnFindInAssetTreeRequested::CreateSP(this, &SContentBrowser::OnFindInAssetTreeRequested) );
AssetContextMenu->SetOnRenameRequested( FAssetContextMenu::FOnRenameRequested::CreateSP(this, &SContentBrowser::OnRenameRequested) );
AssetContextMenu->SetOnRenameFolderRequested( FAssetContextMenu::FOnRenameFolderRequested::CreateSP(this, &SContentBrowser::OnRenameFolderRequested) );
AssetContextMenu->SetOnDuplicateRequested( FAssetContextMenu::FOnDuplicateRequested::CreateSP(this, &SContentBrowser::OnDuplicateRequested) );
AssetContextMenu->SetOnAssetViewRefreshRequested( FAssetContextMenu::FOnAssetViewRefreshRequested::CreateSP( this, &SContentBrowser::OnAssetViewRefreshRequested) );
// Select /Game by default
FSourcesData DefaultSourcesData;
TArray<FString> SelectedPaths;
DefaultSourcesData.PackagePaths.Add(TEXT("/Game"));
SelectedPaths.Add(TEXT("/Game"));
PathViewPtr->SetSelectedPaths(SelectedPaths);
AssetViewPtr->SetSourcesData(DefaultSourcesData);
// Set the initial history data
HistoryManager.AddHistoryData();
// Load settings if they were specified
this->InstanceName = InInstanceName;
LoadSettings(InInstanceName);
// Bindings to manage history when items are deleted
FCollectionManagerModule& CollectionManagerModule = FModuleManager::LoadModuleChecked<FCollectionManagerModule>(TEXT("CollectionManager"));
CollectionManagerModule.Get().OnCollectionRenamed().AddSP(this, &SContentBrowser::HandleCollectionRenamed);
CollectionManagerModule.Get().OnCollectionDestroyed().AddSP(this, &SContentBrowser::HandleCollectionRemoved);
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().OnPathRemoved().AddSP(this, &SContentBrowser::HandlePathRemoved);
// Update the breadcrumb trail path
UpdatePath();
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SContentBrowser::BindCommands()
{
Commands = TSharedPtr< FUICommandList >(new FUICommandList);
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
Commands->MapAction(FGenericCommands::Get().Rename, FUIAction(
FExecuteAction::CreateSP(this, &SContentBrowser::OnRename),
FCanExecuteAction::CreateSP(this, &SContentBrowser::CanRename)
));
Commands->MapAction(FGenericCommands::Get().Delete, FUIAction(
FExecuteAction::CreateSP(this, &SContentBrowser::OnDelete),
FCanExecuteAction::CreateSP(this, &SContentBrowser::CanDelete)
));
Commands->MapAction(FContentBrowserCommands::Get().OpenAssetsOrFolders, FUIAction(
FExecuteAction::CreateSP(this, &SContentBrowser::OnOpenAssetsOrFolders)
));
Commands->MapAction(FContentBrowserCommands::Get().PreviewAssets, FUIAction(
FExecuteAction::CreateSP(this, &SContentBrowser::OnPreviewAssets)
));
Commands->MapAction( FContentBrowserCommands::Get().DirectoryUp, FUIAction(
FExecuteAction::CreateSP( this, &SContentBrowser::OnDirectoryUp )
));
}
FText SContentBrowser::GetHighlightedText() const
{
return TextFilter->GetRawFilterText();
}
void SContentBrowser::CreateNewAsset(const FString& DefaultAssetName, const FString& PackagePath, UClass* AssetClass, UFactory* Factory)
{
AssetViewPtr->CreateNewAsset(DefaultAssetName, PackagePath, AssetClass, Factory);
}
FText SContentBrowser::GetImportTooltipText() const
{
FString CurrentPath = GetCurrentPath();
FText ImportAssetLabel;
if ( !CurrentPath.IsEmpty() )
{
ImportAssetLabel = FText::Format( LOCTEXT( "ImportAsset", "Import to {0}..." ), FText::FromString( CurrentPath ) );
}
else
{
ImportAssetLabel = LOCTEXT( "ImportAsset_NoPath", "Import" );
}
return ImportAssetLabel;
}
FReply SContentBrowser::HandleImportClicked()
{
FString CurrentPath = GetCurrentPath();
if ( ensure( !CurrentPath.IsEmpty() ) )
{
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>( "AssetTools" );
AssetToolsModule.Get().ImportAssets( CurrentPath );
}
return FReply::Handled();
}
void SContentBrowser::SyncToAssets( const TArray<FAssetData>& AssetDataList, const bool bAllowImplicitSync )
{
// Check to see if any of the assets require certain folders to be visible
const UContentBrowserSettings* tmp = GetDefault<UContentBrowserSettings>();
bool bDisplayDev = GetDefault<UContentBrowserSettings>()->GetDisplayDevelopersFolder();
bool bDisplayEngine = GetDefault<UContentBrowserSettings>()->GetDisplayEngineFolder();
bool bDisplayPlugins = GetDefault<UContentBrowserSettings>()->GetDisplayPluginFolders();
if ( !bDisplayDev || !bDisplayEngine || !bDisplayPlugins )
{
bool bRepopulate = false;
for (int32 AssetIdx = AssetDataList.Num() - 1; AssetIdx >= 0 && ( !bDisplayDev || !bDisplayEngine || !bDisplayPlugins ); --AssetIdx)
{
const FAssetData& Item = AssetDataList[AssetIdx];
if ( !bDisplayDev && ContentBrowserUtils::IsDevelopersFolder( Item.PackagePath.ToString() ) )
{
bDisplayDev = true;
GetMutableDefault<UContentBrowserSettings>()->SetDisplayDevelopersFolder(true, true);
bRepopulate = true;
}
else if ( !bDisplayEngine && ContentBrowserUtils::IsEngineFolder( Item.PackagePath.ToString() ) )
{
bDisplayEngine = true;
GetMutableDefault<UContentBrowserSettings>()->SetDisplayEngineFolder(true, true);
bRepopulate = true;
}
else if ( !bDisplayPlugins && ContentBrowserUtils::IsPluginFolder( Item.PackagePath.ToString() ) )
{
bDisplayPlugins = true;
GetMutableDefault<UContentBrowserSettings>()->SetDisplayPluginFolders(true, true);
bRepopulate = true;
}
}
// If we have auto-enabled any flags, force a refresh
if ( bRepopulate )
{
PathViewPtr->Populate();
}
}
// Disable the filter categories
FilterListPtr->DisableFiltersThatHideAssets(AssetDataList);
// Disable the filter search (reset the filter, then clear the search text)
// Note: we have to remove the filter immediately, we can't wait for OnSearchBoxChanged to hit
SetSearchBoxText(FText::GetEmpty());
SearchBoxPtr->SetText(FText::GetEmpty());
// Tell the sources view first so the asset view will be up to date by the time we request the sync
PathViewPtr->SyncToAssets(AssetDataList, bAllowImplicitSync);
AssetViewPtr->SyncToAssets(AssetDataList);
}
void SContentBrowser::SetIsPrimaryContentBrowser (bool NewIsPrimary)
{
bIsPrimaryBrowser = NewIsPrimary;
if ( bIsPrimaryBrowser )
{
SyncGlobalSelectionSet();
}
else
{
USelection* EditorSelection = GEditor->GetSelectedObjects();
if ( ensure( EditorSelection != NULL ) )
{
EditorSelection->DeselectAll();
}
}
}
TSharedPtr<FTabManager> SContentBrowser::GetTabManager() const
{
if ( ContainingTab.IsValid() )
{
return ContainingTab.Pin()->GetTabManager();
}
return NULL;
}
void SContentBrowser::LoadSelectedObjectsIfNeeded()
{
// Get the selected assets in the asset view
const TArray<FAssetData>& SelectedAssets = AssetViewPtr->GetSelectedAssets();
// Load every asset that isn't already in memory
for ( auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt )
{
const FAssetData& AssetData = *AssetIt;
const bool bShowProgressDialog = (!AssetData.IsAssetLoaded() && FEditorFileUtils::IsMapPackageAsset(AssetData.ObjectPath.ToString()));
GWarn->BeginSlowTask(LOCTEXT("LoadingObjects", "Loading Objects..."), bShowProgressDialog);
(*AssetIt).GetAsset();
GWarn->EndSlowTask();
}
// Sync the global selection set if we are the primary browser
if ( bIsPrimaryBrowser )
{
SyncGlobalSelectionSet();
}
}
void SContentBrowser::GetSelectedAssets(TArray<FAssetData>& SelectedAssets)
{
// Make sure the asset data is up to date
AssetViewPtr->ProcessRecentlyLoadedOrChangedAssets();
SelectedAssets = AssetViewPtr->GetSelectedAssets();
}
void SContentBrowser::SaveSettings() const
{
const FString& SettingsString = InstanceName.ToString();
GConfig->SetBool(*SettingsIniSection, *(SettingsString + TEXT(".SourcesExpanded")), bSourcesViewExpanded, GEditorUserSettingsIni);
GConfig->SetBool(*SettingsIniSection, *(SettingsString + TEXT(".Locked")), bIsLocked, GEditorUserSettingsIni);
for(int32 SlotIndex = 0; SlotIndex < PathAssetSplitterPtr->GetChildren()->Num(); SlotIndex++)
{
float SplitterSize = PathAssetSplitterPtr->SlotAt(SlotIndex).SizeValue.Get();
GConfig->SetFloat(*SettingsIniSection, *(SettingsString + FString::Printf(TEXT(".VerticalSplitter.SlotSize%d"), SlotIndex)), SplitterSize, GEditorUserSettingsIni);
}
for(int32 SlotIndex = 0; SlotIndex < PathCollectionSplitterPtr->GetChildren()->Num(); SlotIndex++)
{
float SplitterSize = PathCollectionSplitterPtr->SlotAt(SlotIndex).SizeValue.Get();
GConfig->SetFloat(*SettingsIniSection, *(SettingsString + FString::Printf(TEXT(".HorizontalSplitter.SlotSize%d"), SlotIndex)), SplitterSize, GEditorUserSettingsIni);
}
// Save all our data using the settings string as a key in the user settings ini
FilterListPtr->SaveSettings(GEditorUserSettingsIni, SettingsIniSection, SettingsString);
PathViewPtr->SaveSettings(GEditorUserSettingsIni, SettingsIniSection, SettingsString);
CollectionViewPtr->SaveSettings(GEditorUserSettingsIni, SettingsIniSection, SettingsString);
AssetViewPtr->SaveSettings(GEditorUserSettingsIni, SettingsIniSection, SettingsString);
}
const FName SContentBrowser::GetInstanceName() const
{
return InstanceName;
}
bool SContentBrowser::IsLocked() const
{
return bIsLocked;
}
void SContentBrowser::SetKeyboardFocusOnSearch() const
{
// Focus on the search box
FSlateApplication::Get().SetKeyboardFocus( SearchBoxPtr, EKeyboardFocusCause::SetDirectly );
}
FReply SContentBrowser::OnKeyDown( const FGeometry& MyGeometry, const FKeyboardEvent& InKeyboardEvent )
{
if( Commands->ProcessCommandBindings( InKeyboardEvent ) )
{
return FReply::Handled();
}
return FReply::Unhandled();
}
FReply SContentBrowser::OnPreviewMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
// Clicking in a content browser will shift it to be the primary browser
FContentBrowserSingleton::Get().SetPrimaryContentBrowser(SharedThis(this));
return FReply::Unhandled();
}
FReply SContentBrowser::OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
// Mouse back and forward buttons traverse history
if ( MouseEvent.GetEffectingButton() == EKeys::ThumbMouseButton)
{
HistoryManager.GoBack();
return FReply::Handled();
}
else if ( MouseEvent.GetEffectingButton() == EKeys::ThumbMouseButton2)
{
HistoryManager.GoForward();
return FReply::Handled();
}
return FReply::Unhandled();
}
FReply SContentBrowser::OnMouseButtonDoubleClick( const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent )
{
// Mouse back and forward buttons traverse history
if ( InMouseEvent.GetEffectingButton() == EKeys::ThumbMouseButton)
{
HistoryManager.GoBack();
return FReply::Handled();
}
else if ( InMouseEvent.GetEffectingButton() == EKeys::ThumbMouseButton2)
{
HistoryManager.GoForward();
return FReply::Handled();
}
return FReply::Unhandled();
}
void SContentBrowser::OnContainingTabSavingVisualState() const
{
SaveSettings();
}
void SContentBrowser::OnContainingTabClosed(TSharedRef<SDockTab> DockTab)
{
FContentBrowserSingleton::Get().ContentBrowserClosed( SharedThis(this) );
}
void SContentBrowser::OnContainingTabActivated(TSharedRef<SDockTab> DockTab, ETabActivationCause::Type InActivationCause)
{
if(InActivationCause == ETabActivationCause::UserClickedOnTab)
{
FContentBrowserSingleton::Get().SetPrimaryContentBrowser(SharedThis(this));
}
}
void SContentBrowser::LoadSettings(const FName& InInstanceName)
{
FString SettingsString = InInstanceName.ToString();
// Test to see if we should load legacy settings from a previous instance name
// First make sure there aren't any existing settings with the given instance name
bool TestBool;
if ( !GConfig->GetBool(*SettingsIniSection, *(SettingsString + TEXT(".SourcesExpanded")), TestBool, GEditorUserSettingsIni) )
{
// If there were not any settings and we are Content Browser 1, see if we have any settings under the legacy name "LevelEditorContentBrowser"
if ( InInstanceName.ToString() == TEXT("ContentBrowserTab1") && GConfig->GetBool(*SettingsIniSection, TEXT("LevelEditorContentBrowser.SourcesExpanded"), TestBool, GEditorUserSettingsIni) )
{
// We have found some legacy settings with the old ID, use them. These settings will be saved out to the new id later
SettingsString = TEXT("LevelEditorContentBrowser");
}
// else see if we are Content Browser 2, and see if we have any settings under the legacy name "MajorContentBrowserTab"
else if ( InInstanceName.ToString() == TEXT("ContentBrowserTab2") && GConfig->GetBool(*SettingsIniSection, TEXT("MajorContentBrowserTab.SourcesExpanded"), TestBool, GEditorUserSettingsIni) )
{
// We have found some legacy settings with the old ID, use them. These settings will be saved out to the new id later
SettingsString = TEXT("MajorContentBrowserTab");
}
}
// Now that we have determined the appropriate settings string, actually load the settings
GConfig->GetBool(*SettingsIniSection, *(SettingsString + TEXT(".SourcesExpanded")), bSourcesViewExpanded, GEditorUserSettingsIni);
GConfig->GetBool(*SettingsIniSection, *(SettingsString + TEXT(".Locked")), bIsLocked, GEditorUserSettingsIni);
for(int32 SlotIndex = 0; SlotIndex < PathAssetSplitterPtr->GetChildren()->Num(); SlotIndex++)
{
float SplitterSize = PathAssetSplitterPtr->SlotAt(SlotIndex).SizeValue.Get();
GConfig->GetFloat(*SettingsIniSection, *(SettingsString + FString::Printf(TEXT(".VerticalSplitter.SlotSize%d"), SlotIndex)), SplitterSize, GEditorUserSettingsIni);
PathAssetSplitterPtr->SlotAt(SlotIndex).SizeValue = SplitterSize;
}
for(int32 SlotIndex = 0; SlotIndex < PathCollectionSplitterPtr->GetChildren()->Num(); SlotIndex++)
{
float SplitterSize = PathCollectionSplitterPtr->SlotAt(SlotIndex).SizeValue.Get();
GConfig->GetFloat(*SettingsIniSection, *(SettingsString + FString::Printf(TEXT(".HorizontalSplitter.SlotSize%d"), SlotIndex)), SplitterSize, GEditorUserSettingsIni);
PathCollectionSplitterPtr->SlotAt(SlotIndex).SizeValue = SplitterSize;
}
// Save all our data using the settings string as a key in the user settings ini
FilterListPtr->LoadSettings(GEditorUserSettingsIni, SettingsIniSection, SettingsString);
PathViewPtr->LoadSettings(GEditorUserSettingsIni, SettingsIniSection, SettingsString);
CollectionViewPtr->LoadSettings(GEditorUserSettingsIni, SettingsIniSection, SettingsString);
AssetViewPtr->LoadSettings(GEditorUserSettingsIni, SettingsIniSection, SettingsString);
}
void SContentBrowser::SourcesChanged(const TArray<FString>& SelectedPaths, const TArray<FCollectionNameType>& SelectedCollections)
{
FString NewSource = SelectedPaths.Num() > 0 ? SelectedPaths[0] : (SelectedCollections.Num() > 0 ? SelectedCollections[0].Name.ToString() : TEXT("None"));
UE_LOG(LogContentBrowser, Verbose, TEXT("The content browser source was changed by the sources view to '%s'"), *NewSource);
FSourcesData SourcesData;
for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
{
SourcesData.PackagePaths.Add(FName(*SelectedPaths[PathIdx]));
}
SourcesData.Collections = SelectedCollections;
if (!AssetViewPtr->GetSourcesData().IsEmpty())
{
// Update the current history data to preserve selection if there is a valid SourcesData
HistoryManager.UpdateHistoryData();
}
// Change the filter for the asset view
AssetViewPtr->SetSourcesData(SourcesData);
// Add a new history data now that the source has changed
HistoryManager.AddHistoryData();
// Update the breadcrumb trail path
UpdatePath();
}
void SContentBrowser::FolderEntered(const FString& FolderPath)
{
// set the path view to the incoming path
TArray<FString> SelectedPaths;
SelectedPaths.Add(FolderPath);
PathViewPtr->SetSelectedPaths(SelectedPaths);
PathSelected(FolderPath);
}
void SContentBrowser::PathSelected(const FString& FolderPath)
{
// You may not select both collections and paths
CollectionViewPtr->ClearSelection();
TArray<FString> SelectedPaths = PathViewPtr->GetSelectedPaths();
TArray<FCollectionNameType> SelectedCollections;
SourcesChanged(SelectedPaths, SelectedCollections);
// Notify 'asset path changed' delegate
FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>( TEXT("ContentBrowser") );
FContentBrowserModule::FOnAssetPathChanged& PathChangedDelegate = ContentBrowserModule.GetOnAssetPathChanged();
if(PathChangedDelegate.IsBound())
{
PathChangedDelegate.Broadcast(FolderPath);
}
// Update the context menu's selected paths list
PathContextMenu->SetSelectedPaths(SelectedPaths);
}
TSharedRef<FExtender> SContentBrowser::GetPathContextMenuExtender(const TArray<FString>& InSelectedPaths) const
{
return PathContextMenu->MakePathViewContextMenuExtender(InSelectedPaths);
}
void SContentBrowser::CollectionSelected(const FCollectionNameType& SelectedCollection)
{
// You may not select both collections and paths
PathViewPtr->ClearSelection();
TArray<FCollectionNameType> SelectedCollections = CollectionViewPtr->GetSelectedCollections();
TArray<FString> SelectedPaths;
if( SelectedCollections.Num() == 0 )
{
// just select the game folder
SelectedPaths.Add(TEXT("/Game"));
SourcesChanged(SelectedPaths, SelectedCollections);
}
else
{
SourcesChanged(SelectedPaths, SelectedCollections);
}
}
void SContentBrowser::PathPickerPathSelected(const FString& FolderPath)
{
PathPickerButton->SetIsOpen(false);
if ( !FolderPath.IsEmpty() )
{
TArray<FString> Paths;
Paths.Add(FolderPath);
PathViewPtr->SetSelectedPaths(Paths);
}
PathSelected(FolderPath);
}
void SContentBrowser::PathPickerCollectionSelected(const FCollectionNameType& SelectedCollection)
{
PathPickerButton->SetIsOpen(false);
TArray<FCollectionNameType> Collections;
Collections.Add(SelectedCollection);
CollectionViewPtr->SetSelectedCollections(Collections);
CollectionSelected(SelectedCollection);
}
void SContentBrowser::OnApplyHistoryData ( const FHistoryData& History )
{
PathViewPtr->ApplyHistoryData(History);
CollectionViewPtr->ApplyHistoryData(History);
AssetViewPtr->ApplyHistoryData(History);
// Update the breadcrumb trail path
UpdatePath();
}
void SContentBrowser::OnUpdateHistoryData(FHistoryData& HistoryData) const
{
const FSourcesData& SourcesData = AssetViewPtr->GetSourcesData();
const TArray<FAssetData>& SelectedAssets = AssetViewPtr->GetSelectedAssets();
const FString NewSource = SourcesData.PackagePaths.Num() > 0 ? SourcesData.PackagePaths[0].ToString() : (SourcesData.Collections.Num() > 0 ? SourcesData.Collections[0].Name.ToString() : LOCTEXT("AllAssets", "All Assets").ToString());
HistoryData.HistoryDesc = NewSource;
HistoryData.SourcesData = SourcesData;
HistoryData.SelectedAssets.Empty();
for ( auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt )
{
HistoryData.SelectedAssets.Add(AssetIt->ObjectPath);
}
}
void SContentBrowser::NewAssetRequested(const FString& SelectedPath, TWeakObjectPtr<UClass> FactoryClass)
{
if ( ensure(SelectedPath.Len() > 0) && ensure(FactoryClass.IsValid()) )
{
UFactory* NewFactory = ConstructObject<UFactory>( FactoryClass.Get() );
FEditorDelegates::OnConfigureNewAssetProperties.Broadcast(NewFactory);
if ( NewFactory->ConfigureProperties() )
{
FString DefaultAssetName;
FString PackageNameToUse;
static FName AssetToolsModuleName = FName("AssetTools");
FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>(AssetToolsModuleName);
AssetToolsModule.Get().CreateUniqueAssetName(SelectedPath + TEXT("/") + NewFactory->GetDefaultNewAssetName(), TEXT(""), PackageNameToUse, DefaultAssetName);
CreateNewAsset(DefaultAssetName, SelectedPath, NewFactory->GetSupportedClass(), NewFactory);
}
}
}
void SContentBrowser::NewFolderRequested(const FString& SelectedPath)
{
if( ensure(SelectedPath.Len() > 0) && AssetViewPtr.IsValid() )
{
CreateNewFolder(SelectedPath, FOnCreateNewFolder::CreateSP(AssetViewPtr.Get(), &SAssetView::OnCreateNewFolder));
}
}
void SContentBrowser::SetSearchBoxText(const FText& InSearchText)
{
if (!InSearchText.EqualToCaseIgnored(TextFilter->GetRawFilterText()))
{
TextFilter->SetRawFilterText( InSearchText );
if(InSearchText.IsEmpty())
{
FrontendFilters->Remove(TextFilter);
AssetViewPtr->SetUserSearching(false);
}
else
{
FrontendFilters->Add(TextFilter);
AssetViewPtr->SetUserSearching(true);
}
}
}
void SContentBrowser::OnSearchBoxChanged(const FText& InSearchText)
{
SetSearchBoxText(InSearchText);
// Broadcast 'search box changed' delegate
FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>( TEXT("ContentBrowser") );
ContentBrowserModule.GetOnSearchBoxChanged().Broadcast(InSearchText, bIsPrimaryBrowser);
}
void SContentBrowser::OnSearchBoxCommitted(const FText& InSearchText, ETextCommit::Type CommitInfo)
{
SetSearchBoxText(InSearchText);
}
void SContentBrowser::OnPathClicked( const FString& CrumbData )
{
FSourcesData SourcesData = AssetViewPtr->GetSourcesData();
if ( SourcesData.Collections.Num() > 0 )
{
// Collection crumb was clicked. Since we don't have a hierarchy of collections, this does nothing.
}
else if ( SourcesData.PackagePaths.Num() == 0 )
{
// No collections or paths are selected. This is "All Assets". Don't change the path when this is clicked.
}
else if ( SourcesData.PackagePaths.Num() > 1 || SourcesData.PackagePaths[0].ToString() != CrumbData )
{
// More than one path is selected or the crumb that was clicked is not the same path as the current one. Change the path.
TArray<FString> SelectedPaths;
SelectedPaths.Add(CrumbData);
PathViewPtr->SetSelectedPaths(SelectedPaths);
SourcesChanged(SelectedPaths, TArray<FCollectionNameType>());
}
}
void SContentBrowser::OnPathMenuItemClicked(FString ClickedPath)
{
OnPathClicked( ClickedPath );
}
TSharedPtr<SWidget> SContentBrowser::OnGetCrumbDelimiterContent(const FString& CrumbData) const
{
FSourcesData SourcesData = AssetViewPtr->GetSourcesData();
TSharedPtr<SWidget> Widget = SNullWidget::NullWidget;
if( SourcesData.PackagePaths.Num() > 0 )
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
TArray<FString> SubPaths;
const bool bRecurse = false;
AssetRegistry.GetSubPaths( CrumbData, SubPaths, bRecurse );
if( SubPaths.Num() > 0 )
{
FMenuBuilder MenuBuilder( true, NULL );
for( int32 PathIndex = 0; PathIndex < SubPaths.Num(); ++PathIndex )
{
const FString& SubPath = SubPaths[PathIndex];
// For displaying in the menu cut off the parent path since it is redundant
FString PathWithoutParent = SubPath.RightChop( CrumbData.Len() + 1 );
MenuBuilder.AddMenuEntry(
FText::FromString(PathWithoutParent),
FText::GetEmpty(),
FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.BreadcrumbPathPickerFolder"),
FUIAction(FExecuteAction::CreateSP(this, &SContentBrowser::OnPathMenuItemClicked, SubPath)));
}
// Do not allow the menu to become too large if there are many directories
Widget =
SNew( SVerticalBox )
+SVerticalBox::Slot()
.MaxHeight( 400.0f )
[
MenuBuilder.MakeWidget()
];
}
}
return Widget;
}
TSharedRef<SWidget> SContentBrowser::GetPathPickerContent()
{
FPathPickerConfig PathPickerConfig;
FSourcesData SourcesData = AssetViewPtr->GetSourcesData();
if ( SourcesData.PackagePaths.Num() > 0 )
{
PathPickerConfig.DefaultPath = SourcesData.PackagePaths[0].ToString();
}
PathPickerConfig.OnPathSelected = FOnPathSelected::CreateSP(this, &SContentBrowser::PathPickerPathSelected);
PathPickerConfig.bAllowContextMenu = false;
return SNew(SBox)
.WidthOverride(300)
.HeightOverride(500)
.Padding(4)
[
SNew(SVerticalBox)
// Path Picker
+SVerticalBox::Slot()
.FillHeight(1.f)
[
FContentBrowserSingleton::Get().CreatePathPicker(PathPickerConfig)
]
// Collection View
+SVerticalBox::Slot()
.AutoHeight()
.Padding(0, 6, 0, 0)
[
SNew(SCollectionView)
.AllowCollectionButtons(false)
.OnCollectionSelected(this, &SContentBrowser::PathPickerCollectionSelected)
.AllowContextMenu(false)
]
];
}
FString SContentBrowser::GetCurrentPath() const
{
FString CurrentPath;
const FSourcesData& SourcesData = AssetViewPtr->GetSourcesData();
if ( SourcesData.PackagePaths.Num() > 0 && SourcesData.PackagePaths[0] != NAME_None )
{
CurrentPath = SourcesData.PackagePaths[0].ToString();
}
return CurrentPath;
}
TSharedRef<SWidget> SContentBrowser::MakeCreateAssetContextMenu()
{
FString CurrentPath = GetCurrentPath();
// Get all menu extenders for this context menu from the content browser module
FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>( TEXT("ContentBrowser") );
TArray<FContentBrowserMenuExtender> MenuExtenderDelegates = ContentBrowserModule.GetAllAssetContextMenuExtenders();
TArray<TSharedPtr<FExtender>> Extenders;
for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
{
if (MenuExtenderDelegates[i].IsBound())
{
Extenders.Add(MenuExtenderDelegates[i].Execute());
}
}
TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, NULL, MenuExtender);
// Only add "New Folder" item if we do not have a collection selected
FNewAssetContextMenu::FOnNewFolderRequested OnNewFolderRequested;
if (CollectionViewPtr->GetSelectedCollections().Num() == 0)
{
OnNewFolderRequested = FNewAssetContextMenu::FOnNewFolderRequested::CreateSP(this, &SContentBrowser::NewFolderRequested);
}
FNewAssetContextMenu::MakeContextMenu(
MenuBuilder,
CurrentPath,
FNewAssetContextMenu::FOnNewAssetRequested::CreateSP(this, &SContentBrowser::NewAssetRequested),
OnNewFolderRequested);
FDisplayMetrics DisplayMetrics;
FSlateApplication::Get().GetDisplayMetrics( DisplayMetrics );
const FVector2D DisplaySize(
DisplayMetrics.PrimaryDisplayWorkAreaRect.Right - DisplayMetrics.PrimaryDisplayWorkAreaRect.Left,
DisplayMetrics.PrimaryDisplayWorkAreaRect.Bottom - DisplayMetrics.PrimaryDisplayWorkAreaRect.Top );
return
SNew(SVerticalBox)
+SVerticalBox::Slot()
.MaxHeight(DisplaySize.Y * 0.5)
[
MenuBuilder.MakeWidget()
];
}
FString SContentBrowser::GetNewAssetToolTipText() const
{
const FSourcesData& SourcesData = AssetViewPtr->GetSourcesData();
// At least one source is selected
if (SourcesData.PackagePaths.Num() > 0)
{
return FString::Printf( *LOCTEXT("CreateAssetToolTip", "Create an asset in %s.").ToString(), *SourcesData.PackagePaths[0].ToString() );
}
else
{
return FString();
}
}
TSharedRef<SWidget> SContentBrowser::MakeAddFilterMenu()
{
return FilterListPtr->ExternalMakeAddFilterMenu();
}
TSharedPtr<SWidget> SContentBrowser::GetFilterContextMenu()
{
return FilterListPtr->ExternalMakeAddFilterMenu();
}
FReply SContentBrowser::OnSaveClicked()
{
ContentBrowserUtils::SaveDirtyPackages();
return FReply::Handled();
}
void SContentBrowser::OnAssetSelectionChanged(const FAssetData& SelectedAsset)
{
if ( bIsPrimaryBrowser )
{
SyncGlobalSelectionSet();
}
// Notify 'asset selection changed' delegate
FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>( TEXT("ContentBrowser") );
FContentBrowserModule::FOnAssetSelectionChanged& AssetSelectionChangedDelegate = ContentBrowserModule.GetOnAssetSelectionChanged();
const TArray<FAssetData>& SelectedAssets = AssetViewPtr->GetSelectedAssets();
AssetContextMenu->SetSelectedAssets(SelectedAssets);
if(AssetSelectionChangedDelegate.IsBound())
{
AssetSelectionChangedDelegate.Broadcast(SelectedAssets, bIsPrimaryBrowser);
}
}
void SContentBrowser::OnAssetsActivated(const TArray<FAssetData>& ActivatedAssets, EAssetTypeActivationMethod::Type ActivationMethod)
{
TMap< TSharedRef<IAssetTypeActions>, TArray<UObject*> > TypeActionsToObjects;
TArray<UObject*> ObjectsWithoutTypeActions;
const FText LoadingTemplate = LOCTEXT("LoadingAssetName", "Loading {0}...");
const FText DefaultText = ActivatedAssets.Num() == 1 ? FText::Format(LoadingTemplate, FText::FromName(ActivatedAssets[0].AssetName)) : LOCTEXT("LoadingObjects", "Loading Objects...");
FScopedSlowTask SlowTask(100, DefaultText);
// Iterate over all activated assets to map them to AssetTypeActions.
// This way individual asset type actions will get a batched list of assets to operate on
for ( auto AssetIt = ActivatedAssets.CreateConstIterator(); AssetIt; ++AssetIt )
{
const FAssetData& AssetData = *AssetIt;
if (!AssetData.IsAssetLoaded() && FEditorFileUtils::IsMapPackageAsset(AssetData.ObjectPath.ToString()))
{
SlowTask.MakeDialog();
}
SlowTask.EnterProgressFrame(75.f/ActivatedAssets.Num(), FText::Format(LoadingTemplate, FText::FromName(AssetData.AssetName)));
UObject* Asset = (*AssetIt).GetAsset();
if ( Asset != NULL )
{
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
TWeakPtr<IAssetTypeActions> AssetTypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass(Asset->GetClass());
if ( AssetTypeActions.IsValid() )
{
// Add this asset to the list associated with the asset type action object
TArray<UObject*>& ObjList = TypeActionsToObjects.FindOrAdd(AssetTypeActions.Pin().ToSharedRef());
ObjList.AddUnique(Asset);
}
else
{
ObjectsWithoutTypeActions.AddUnique(Asset);
}
}
}
// Now that we have created our map, activate all the lists of objects for each asset type action.
for ( auto TypeActionsIt = TypeActionsToObjects.CreateConstIterator(); TypeActionsIt; ++TypeActionsIt )
{
SlowTask.EnterProgressFrame(25.f/TypeActionsToObjects.Num());
const TSharedRef<IAssetTypeActions>& TypeActions = TypeActionsIt.Key();
const TArray<UObject*>& ObjList = TypeActionsIt.Value();
TypeActions->AssetsActivated(ObjList, ActivationMethod);
}
// Finally, open a simple asset editor for all assets which do not have asset type actions if activating with enter or double click
if ( ActivationMethod == EAssetTypeActivationMethod::DoubleClicked || ActivationMethod == EAssetTypeActivationMethod::Opened )
{
ContentBrowserUtils::OpenEditorForAsset(ObjectsWithoutTypeActions);
}
}
TSharedPtr<SWidget> SContentBrowser::OnGetAssetContextMenu(const TArray<FAssetData>& SelectedAssets)
{
// If a class is selected do not open a context menu
for(auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt)
{
if( AssetIt->AssetClass == NAME_Class )
{
return NULL;
}
}
// If the Classes folder is selected do not open a context menu
const TArray<FString> SelectedPaths = PathViewPtr->GetSelectedPaths();
if(SelectedPaths.Contains(TEXT("/Classes")))
{
return NULL;
}
if ( SelectedAssets.Num() == 0 )
{
return MakeCreateAssetContextMenu();
}
else
{
return AssetContextMenu->MakeContextMenu(SelectedAssets, AssetViewPtr->GetSourcesData(), Commands);
}
}
FReply SContentBrowser::ToggleLockClicked()
{
bIsLocked = !bIsLocked;
return FReply::Handled();
}
const FSlateBrush* SContentBrowser::GetToggleLockImage() const
{
if ( bIsLocked )
{
return FEditorStyle::GetBrush("ContentBrowser.LockButton_Locked");
}
else
{
return FEditorStyle::GetBrush("ContentBrowser.LockButton_Unlocked");
}
}
EVisibility SContentBrowser::GetSourcesViewVisibility() const
{
return bSourcesViewExpanded ? EVisibility::Visible : EVisibility::Collapsed;
}
const FSlateBrush* SContentBrowser::GetSourcesToggleImage() const
{
if ( bSourcesViewExpanded )
{
return FEditorStyle::GetBrush("ContentBrowser.HideSourcesView");
}
else
{
return FEditorStyle::GetBrush("ContentBrowser.ShowSourcesView");
}
}
FReply SContentBrowser::SourcesViewExpandClicked()
{
bSourcesViewExpanded = !bSourcesViewExpanded;
// Notify 'Soureces View Expanded' delegate
FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>( TEXT("ContentBrowser") );
FContentBrowserModule::FOnSourcesViewChanged& SourcesViewChangedDelegate = ContentBrowserModule.GetOnSourcesViewChanged();
if(SourcesViewChangedDelegate.IsBound())
{
SourcesViewChangedDelegate.Broadcast(bSourcesViewExpanded);
}
return FReply::Handled();
}
EVisibility SContentBrowser::GetPathExpanderVisibility() const
{
return bSourcesViewExpanded ? EVisibility::Collapsed : EVisibility::Visible;
}
FReply SContentBrowser::BackClicked()
{
HistoryManager.GoBack();
return FReply::Handled();
}
FReply SContentBrowser::ForwardClicked()
{
HistoryManager.GoForward();
return FReply::Handled();
}
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
bool SContentBrowser::CanRename() const
{
const TArray<TSharedPtr<FAssetViewItem>>& SelectedItems = AssetViewPtr->GetSelectedItems();
if (SelectedItems.Num() > 0)
{
return AssetContextMenu->CanExecuteRename();
}
else
{
const TArray<FString>& SelectedPaths = PathViewPtr->GetSelectedPaths();
if (SelectedPaths.Num() > 0)
{
return PathContextMenu->CanExecuteRename();
}
}
return false;
}
void SContentBrowser::OnRename()
{
const TArray<TSharedPtr<FAssetViewItem>>& SelectedItems = AssetViewPtr->GetSelectedItems();
if (SelectedItems.Num() > 0)
{
AssetContextMenu->ExecuteRename();
}
else
{
const TArray<FString>& SelectedPaths = PathViewPtr->GetSelectedPaths();
if (SelectedPaths.Num() > 0)
{
PathContextMenu->ExecuteRename();
}
}
}
bool SContentBrowser::CanDelete() const
{
const TArray<TSharedPtr<FAssetViewItem>>& SelectedItems = AssetViewPtr->GetSelectedItems();
if (SelectedItems.Num() > 0)
{
return AssetContextMenu->CanExecuteDelete();
}
else
{
const TArray<FString>& SelectedPaths = PathViewPtr->GetSelectedPaths();
if (SelectedPaths.Num() > 0)
{
return PathContextMenu->CanExecuteDelete();
}
}
return false;
}
void SContentBrowser::OnDelete()
{
const TArray<TSharedPtr<FAssetViewItem>>& SelectedItems = AssetViewPtr->GetSelectedItems();
if (SelectedItems.Num() > 0)
{
AssetContextMenu->ExecuteDelete();
}
else
{
const TArray<FString>& SelectedPaths = PathViewPtr->GetSelectedPaths();
if (SelectedPaths.Num() > 0)
{
PathContextMenu->ExecuteDelete();
}
}
}
void SContentBrowser::OnOpenAssetsOrFolders()
{
AssetViewPtr->OnOpenAssetsOrFolders();
}
void SContentBrowser::OnPreviewAssets()
{
AssetViewPtr->OnPreviewAssets();
}
FReply SContentBrowser::OnDirectoryUpClicked()
{
OnDirectoryUp();
return FReply::Handled();
}
void SContentBrowser::OnDirectoryUp()
{
TArray<FString> SelectedPaths = PathViewPtr->GetSelectedPaths();
if(SelectedPaths.Num() == 1 && !ContentBrowserUtils::IsAssetRootDir(SelectedPaths[0]))
{
FString ParentDir = SelectedPaths[0] / TEXT("..");
FPaths::CollapseRelativeDirectories(ParentDir);
FolderEntered(ParentDir);
}
}
bool SContentBrowser::IsBackEnabled() const
{
return HistoryManager.CanGoBack();
}
bool SContentBrowser::IsForwardEnabled() const
{
return HistoryManager.CanGoForward();
}
bool SContentBrowser::CanExecuteDirectoryUp() const
{
TArray<FString> SelectedPaths = PathViewPtr->GetSelectedPaths();
return (SelectedPaths.Num() == 1 && !ContentBrowserUtils::IsAssetRootDir(SelectedPaths[0]));
}
FString SContentBrowser::GetHistoryBackTooltip() const
{
if ( HistoryManager.CanGoBack() )
{
return FString::Printf( *LOCTEXT("HistoryBackTooltip", "Back to %s").ToString(), *HistoryManager.GetBackDesc() );
}
else
{
return FString();
}
}
FString SContentBrowser::GetHistoryForwardTooltip() const
{
if ( HistoryManager.CanGoForward() )
{
return FString::Printf( *LOCTEXT("HistoryForwardTooltip", "Forward to %s").ToString(), *HistoryManager.GetForwardDesc() );
}
else
{
return FString();
}
}
FText SContentBrowser::GetDirectoryUpTooltip() const
{
TArray<FString> SelectedPaths = PathViewPtr->GetSelectedPaths();
if(SelectedPaths.Num() == 1 && !ContentBrowserUtils::IsAssetRootDir(SelectedPaths[0]))
{
FString ParentDir = SelectedPaths[0] / TEXT("..");
FPaths::CollapseRelativeDirectories(ParentDir);
return FText::Format(LOCTEXT("DirectoryUpTooltip", "Up to {0}"), FText::FromString(ParentDir) );
}
return FText();
}
EVisibility SContentBrowser::GetDirectoryUpToolTipVisibility() const
{
EVisibility ToolTipVisibility = EVisibility::Collapsed;
// if we have text in our tooltip, make it visible.
if(GetDirectoryUpTooltip().IsEmpty() == false)
{
ToolTipVisibility = EVisibility::Visible;
}
return ToolTipVisibility;
}
void SContentBrowser::SyncGlobalSelectionSet()
{
USelection* EditorSelection = GEditor->GetSelectedObjects();
if ( !ensure( EditorSelection != NULL ) )
{
return;
}
// Get the selected assets in the asset view
const TArray<FAssetData>& SelectedAssets = AssetViewPtr->GetSelectedAssets();
EditorSelection->BeginBatchSelectOperation();
{
TSet< UObject* > SelectedObjects;
// Lets see what the user has selected and add any new selected objects to the global selection set
for ( auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt )
{
// Grab the object if it is loaded
if ( (*AssetIt).IsAssetLoaded() )
{
UObject* FoundObject = (*AssetIt).GetAsset();
if( FoundObject != NULL && FoundObject->GetClass() != UObjectRedirector::StaticClass() )
{
SelectedObjects.Add( FoundObject );
// Select this object!
EditorSelection->Select( FoundObject );
}
}
}
// Now we'll build a list of objects that need to be removed from the global selection set
for( int32 CurEditorObjectIndex = 0; CurEditorObjectIndex < EditorSelection->Num(); ++CurEditorObjectIndex )
{
UObject* CurEditorObject = EditorSelection->GetSelectedObject( CurEditorObjectIndex );
if( CurEditorObject != NULL )
{
if( !SelectedObjects.Contains( CurEditorObject ) )
{
EditorSelection->Deselect( CurEditorObject );
}
}
}
}
EditorSelection->EndBatchSelectOperation();
}
void SContentBrowser::UpdatePath()
{
FSourcesData SourcesData = AssetViewPtr->GetSourcesData();
PathBreadcrumbTrail->ClearCrumbs();
if ( SourcesData.PackagePaths.Num() > 0 )
{
TArray<FString> Crumbs;
SourcesData.PackagePaths[0].ToString().ParseIntoArray(&Crumbs, TEXT("/"), true);
FString CrumbPath = TEXT("/");
for ( auto CrumbIt = Crumbs.CreateConstIterator(); CrumbIt; ++CrumbIt )
{
CrumbPath += *CrumbIt;
PathBreadcrumbTrail->PushCrumb(FText::FromString(*CrumbIt), CrumbPath);
CrumbPath += TEXT("/");
}
}
else if ( SourcesData.Collections.Num() > 0 )
{
const FString CollectionName = SourcesData.Collections[0].Name.ToString();
const FString CollectionType = FString::FromInt(SourcesData.Collections[0].Type);
const FString CrumbData = CollectionName + TEXT("?") + CollectionType;
FFormatNamedArguments Args;
Args.Add(TEXT("CollectionName"), FText::FromString(CollectionName));
const FText DisplayName = FText::Format(LOCTEXT("CollectionPathIndicator", "{CollectionName} (Collection)"), Args);
PathBreadcrumbTrail->PushCrumb(DisplayName, CrumbData);
}
else
{
PathBreadcrumbTrail->PushCrumb(LOCTEXT("AllAssets", "All Assets"), TEXT(""));
}
}
void SContentBrowser::OnFilterChanged()
{
FARFilter Filter = FilterListPtr->GetCombinedBackendFilter();
AssetViewPtr->SetBackendFilter( Filter );
// Notify 'filter changed' delegate
FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>( TEXT("ContentBrowser") );
ContentBrowserModule.GetOnFilterChanged().Broadcast(Filter, bIsPrimaryBrowser);
}
FString SContentBrowser::GetPathText() const
{
FString PathLabelText;
if ( IsFilteredBySource() )
{
const FSourcesData& SourcesData = AssetViewPtr->GetSourcesData();
// At least one source is selected
int32 NumSources = SourcesData.PackagePaths.Num() + SourcesData.Collections.Num();
if (NumSources > 0)
{
PathLabelText = SourcesData.PackagePaths.Num() > 0 ? SourcesData.PackagePaths[0].ToString() : SourcesData.Collections[0].Name.ToString();
if (NumSources > 1)
{
PathLabelText += FString::Printf(*LOCTEXT("MultipleSourcesSuffix", " and %d others...").ToString(), NumSources - 1);
}
}
}
else
{
PathLabelText = LOCTEXT("AllAssets", "All Assets").ToString();
}
return PathLabelText;
}
bool SContentBrowser::IsFilteredBySource() const
{
const FSourcesData& SourcesData = AssetViewPtr->GetSourcesData();
return SourcesData.PackagePaths.Num() != 0 || SourcesData.Collections.Num() != 0;
}
bool SContentBrowser::IsAssetPathSelected() const
{
return AssetViewPtr->IsAssetPathSelected();
}
void SContentBrowser::OnAssetRenameCommitted(const TArray<FAssetData>& Assets)
{
// After a rename is committed we allow an implicit sync so as not to
// disorientate the user if they are looking at a parent folder
SyncToAssets(Assets, /*bAllowImplicitSync*/true);
}
void SContentBrowser::OnFindInAssetTreeRequested(const TArray<FAssetData>& AssetsToFind)
{
SyncToAssets(AssetsToFind);
}
void SContentBrowser::OnRenameRequested(const FAssetData& AssetData)
{
AssetViewPtr->RenameAsset(AssetData);
}
void SContentBrowser::OnRenameFolderRequested(const FString& FolderToRename)
{
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
const TArray<FString>& SelectedFolders = AssetViewPtr->GetSelectedFolders();
if (SelectedFolders.Num() > 0)
{
AssetViewPtr->RenameFolder(FolderToRename);
}
else
{
const TArray<FString>& SelectedPaths = PathViewPtr->GetSelectedPaths();
if (SelectedPaths.Num() > 0)
{
PathViewPtr->RenameFolder(FolderToRename);
}
}
}
void SContentBrowser::OnOpenedFolderDeleted()
{
// Since the contents of the asset view have just been deleted, set the selected path to the default "/Game"
TArray<FString> DefaultSelectedPaths;
DefaultSelectedPaths.Add(TEXT("/Game"));
PathViewPtr->SetSelectedPaths(DefaultSelectedPaths);
FSourcesData DefaultSourcesData;
DefaultSourcesData.PackagePaths.Add(TEXT("/Game"));
AssetViewPtr->SetSourcesData(DefaultSourcesData);
}
void SContentBrowser::OnDuplicateRequested(const TWeakObjectPtr<UObject>& OriginalObject)
{
UObject* Object = OriginalObject.Get();
if ( Object )
{
AssetViewPtr->DuplicateAsset(FPackageName::GetLongPackagePath(Object->GetOutermost()->GetName()), OriginalObject);
}
}
void SContentBrowser::OnAssetViewRefreshRequested()
{
AssetViewPtr->RequestSlowFullListRefresh();
}
void SContentBrowser::HandleCollectionRemoved(const FCollectionNameType& Collection)
{
AssetViewPtr->SetSourcesData(FSourcesData());
auto RemoveHistoryDelegate = [&](const FHistoryData& HistoryData)
{
return (HistoryData.SourcesData.Collections.Num() == 1 &&
HistoryData.SourcesData.PackagePaths.Num() == 0 &&
HistoryData.SourcesData.Collections.Contains(Collection));
};
HistoryManager.RemoveHistoryData(RemoveHistoryDelegate);
}
void SContentBrowser::HandleCollectionRenamed(const FCollectionNameType& OriginalCollection, const FCollectionNameType& NewCollection)
{
return HandleCollectionRemoved(OriginalCollection);
}
void SContentBrowser::HandlePathRemoved(const FString& Path)
{
const FName PathName(*Path);
auto RemoveHistoryDelegate = [&](const FHistoryData& HistoryData)
{
return (HistoryData.SourcesData.PackagePaths.Num() == 1 &&
HistoryData.SourcesData.Collections.Num() == 0 &&
HistoryData.SourcesData.PackagePaths.Contains(PathName));
};
HistoryManager.RemoveHistoryData(RemoveHistoryDelegate);
}
FText SContentBrowser::GetSearchAssetsHintText() const
{
if (PathViewPtr.IsValid())
{
TArray<FString> Paths = PathViewPtr->GetSelectedPaths();
if (Paths.Num() != 0)
{
FString SearchHint = "Search ";
for(int i = 0; i < Paths.Num(); i++)
{
SearchHint += FPaths::GetCleanFilename(Paths[i]);
if (i + 1 < Paths.Num())
SearchHint += ", ";
}
return FText::FromString(SearchHint);
}
}
return NSLOCTEXT( "ContentBrowser", "SearchBoxHint", "Search Assets" );
}
TArray<FString> SContentBrowser::GetAssetSearchSuggestions() const
{
TArray<FString> AllSuggestions;
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
TArray< TWeakPtr<IAssetTypeActions> > AssetTypeActionsList;
AssetToolsModule.Get().GetAssetTypeActionsList(AssetTypeActionsList);
for ( auto TypeActionsIt = AssetTypeActionsList.CreateConstIterator(); TypeActionsIt; ++TypeActionsIt )
{
if ( (*TypeActionsIt).IsValid() )
{
const TSharedPtr<IAssetTypeActions> TypeActions = (*TypeActionsIt).Pin();
AllSuggestions.Add( TypeActions->GetSupportedClass()->GetName() );
}
}
return AllSuggestions;
}
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
TSharedPtr<SWidget> SContentBrowser::GetFolderContextMenu(const TArray<FString>& SelectedPaths, FContentBrowserMenuExtender_SelectedPaths InMenuExtender, FOnCreateNewFolder InOnCreateNewFolder, bool bPathView)
{
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
// Clear any selection in the asset view, as it'll conflict with other view info
// This is important for determining which context menu may be open based on the asset selection for rename/delete operations
if (bPathView)
{
AssetViewPtr->ClearSelection();
}
TSharedPtr<FExtender> Extender;
if(InMenuExtender.IsBound())
{
Extender = InMenuExtender.Execute(SelectedPaths);
}
const bool bInShouldCloseWindowAfterSelection = true;
FMenuBuilder MenuBuilder(bInShouldCloseWindowAfterSelection, Commands, Extender, true);
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
// New Folder
MenuBuilder.AddMenuEntry(
LOCTEXT("NewFolder", "New Folder"),
LOCTEXT("NewSubFolderTooltip", "Creates a new sub-folder in this folder."),
FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.NewFolderIcon"),
FUIAction( FExecuteAction::CreateSP( this, &SContentBrowser::CreateNewFolder, SelectedPaths.Num() > 0 ? SelectedPaths[0] : FString(), InOnCreateNewFolder ) ),
"NewFolder"
);
return MenuBuilder.MakeWidget();
}
void SContentBrowser::CreateNewFolder(FString FolderPath, FOnCreateNewFolder InOnCreateNewFolder)
{
// Create a valid base name for this folder
FText DefaultFolderBaseName = LOCTEXT("DefaultFolderName", "NewFolder");
FText DefaultFolderName = DefaultFolderBaseName;
int32 NewFolderPostfix = 1;
while(ContentBrowserUtils::DoesFolderExist(FolderPath / DefaultFolderName.ToString()))
{
DefaultFolderName = FText::Format(LOCTEXT("DefaultFolderNamePattern", "{0}{1}"), DefaultFolderBaseName, FText::AsNumber(NewFolderPostfix));
NewFolderPostfix++;
}
InOnCreateNewFolder.ExecuteIfBound(DefaultFolderName.ToString(), FolderPath);
}
#undef LOCTEXT_NAMESPACE