Files
UnrealEngineUWP/Engine/Source/Editor/ContentBrowser/Private/SCollectionView.cpp
Jamie Dale e41c9d884d Optimized the Content Browser text filter, and added support for matching collection names in simple string searches
UETOOL-332 - Collections 2.0

Converted FFrontendFilter_Text to use FTextFilterExpressionEvaluator directly (rather than via TTextFilter), as this allows it better control over memory re-use (in this case, re-using an array of collection names, as well as some strings).

Added FTextFilterString, which is used internally by the compiled text filter, and also by the comparison functions. This stores the string given to it as uppercase, which allows us to quickly perform case-insensitive string comparisons since we can actually do normal case-sensitive comparisons instead as everything is uppercase.

[CL 2599097 by Jamie Dale in Main branch]
2015-06-24 12:29:41 -04:00

1331 lines
47 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "ContentBrowserPCH.h"
#include "CollectionAssetManagement.h"
#include "CollectionViewTypes.h"
#include "CollectionContextMenu.h"
#include "DragAndDrop/AssetDragDropOp.h"
#include "DragAndDrop/CollectionDragDropOp.h"
#include "ObjectTools.h"
#include "SourcesViewWidgets.h"
#include "ContentBrowserModule.h"
#include "SExpandableArea.h"
#include "SSearchBox.h"
#define LOCTEXT_NAMESPACE "ContentBrowser"
namespace CollectionViewFilter
{
void GetBasicStrings(const FCollectionItem& InCollection, TArray<FString>& OutBasicStrings)
{
OutBasicStrings.Add(InCollection.CollectionName.ToString());
}
bool TestComplexExpression(const FCollectionItem& InCollection, const FName& InKey, const FTextFilterString& InValue, ETextFilterComparisonOperation InComparisonOperation, ETextFilterTextComparisonMode InTextComparisonMode)
{
static const FName NameKeyName = "Name";
static const FName TypeKeyName = "Type";
// Handle the collection name
if (InKey == NameKeyName)
{
// Names can only work with Equal or NotEqual type tests
if (InComparisonOperation != ETextFilterComparisonOperation::Equal && InComparisonOperation != ETextFilterComparisonOperation::NotEqual)
{
return false;
}
const bool bIsMatch = TextFilterUtils::TestBasicStringExpression(InCollection.CollectionName.ToString(), InValue, InTextComparisonMode);
return (InComparisonOperation == ETextFilterComparisonOperation::Equal) ? bIsMatch : !bIsMatch;
}
// Handle the collection type
if (InKey == TypeKeyName)
{
// Types can only work with Equal or NotEqual type tests
if (InComparisonOperation != ETextFilterComparisonOperation::Equal && InComparisonOperation != ETextFilterComparisonOperation::NotEqual)
{
return false;
}
const bool bIsMatch = TextFilterUtils::TestBasicStringExpression(ECollectionShareType::ToString(InCollection.CollectionType), InValue, InTextComparisonMode);
return (InComparisonOperation == ETextFilterComparisonOperation::Equal) ? bIsMatch : !bIsMatch;
}
return false;
}
} // namespace CollectionViewFilter
void SCollectionView::Construct( const FArguments& InArgs )
{
OnCollectionSelected = InArgs._OnCollectionSelected;
bAllowCollectionButtons = InArgs._AllowCollectionButtons;
bAllowRightClickMenu = InArgs._AllowRightClickMenu;
bAllowCollectionDrag = InArgs._AllowCollectionDrag;
bDraggedOver = false;
FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
CollectionManagerModule.Get().OnCollectionCreated().AddSP( this, &SCollectionView::HandleCollectionCreated );
CollectionManagerModule.Get().OnCollectionRenamed().AddSP( this, &SCollectionView::HandleCollectionRenamed );
CollectionManagerModule.Get().OnCollectionReparented().AddSP( this, &SCollectionView::HandleCollectionReparented );
CollectionManagerModule.Get().OnCollectionDestroyed().AddSP( this, &SCollectionView::HandleCollectionDestroyed );
Commands = TSharedPtr< FUICommandList >(new FUICommandList);
CollectionContextMenu = MakeShareable(new FCollectionContextMenu( SharedThis(this) ));
CollectionContextMenu->BindCommands(Commands);
CollectionItemTextFilter = MakeShareable(new FCollectionItemTextFilter(
FCollectionItemTextFilter::FItemToStringArray::CreateStatic(&CollectionViewFilter::GetBasicStrings),
FCollectionItemTextFilter::FItemTestComplexExpression::CreateStatic(&CollectionViewFilter::TestComplexExpression)
));
CollectionItemTextFilter->OnChanged().AddSP(this, &SCollectionView::UpdateFilteredCollectionItems);
if ( InArgs._AllowQuickAssetManagement )
{
QuickAssetManagement = MakeShareable(new FCollectionAssetManagement());
}
FOnContextMenuOpening CollectionListContextMenuOpening;
if ( InArgs._AllowContextMenu )
{
CollectionListContextMenuOpening = FOnContextMenuOpening::CreateSP( this, &SCollectionView::MakeCollectionTreeContextMenu );
}
PreventSelectionChangedDelegateCount = 0;
TSharedRef< SWidget > HeaderContent = SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.Padding(0.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
SNew(STextBlock)
.Font( FEditorStyle::GetFontStyle("ContentBrowser.SourceTitleFont") )
.Text( LOCTEXT("CollectionsListTitle", "Collections") )
.Visibility( this, &SCollectionView::GetCollectionsTitleTextVisibility )
]
+ SHorizontalBox::Slot()
[
SAssignNew(SearchBoxPtr, SSearchBox)
.HintText( LOCTEXT( "CollectionsViewSearchBoxHint", "Search Collections" ) )
.OnTextChanged( this, &SCollectionView::SetCollectionsSearchFilterText )
.Visibility( this, &SCollectionView::GetCollectionsSearchBoxVisibility )
]
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(2.0f, 0.0f, 0.0f, 0.0f)
[
SNew(SButton)
.ButtonStyle(FEditorStyle::Get(), "FlatButton")
.ToolTipText(LOCTEXT("AddCollectionButtonTooltip", "Add a collection."))
.OnClicked(this, &SCollectionView::MakeAddCollectionMenu)
.ContentPadding( FMargin(2, 2) )
.Visibility(this, &SCollectionView::GetAddCollectionButtonVisibility)
[
SNew(SImage) .Image( FEditorStyle::GetBrush("ContentBrowser.AddCollectionButtonIcon") )
]
];
TSharedRef< SWidget > BodyContent = SNew(SVerticalBox)
// Separator
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SSeparator)
]
// Collections tree
+SVerticalBox::Slot()
.FillHeight(1.f)
[
SAssignNew(CollectionTreePtr, STreeView< TSharedPtr<FCollectionItem> >)
.TreeItemsSource(&VisibleRootCollectionItems)
.OnGenerateRow(this, &SCollectionView::GenerateCollectionRow)
.OnGetChildren(this, &SCollectionView::GetCollectionItemChildren)
.ItemHeight(18)
.SelectionMode(ESelectionMode::Multi)
.OnSelectionChanged(this, &SCollectionView::CollectionSelectionChanged)
.OnContextMenuOpening(CollectionListContextMenuOpening)
.OnItemScrolledIntoView(this, &SCollectionView::CollectionItemScrolledIntoView)
.ClearSelectionOnClick(false)
.Visibility(this, &SCollectionView::GetCollectionTreeVisibility)
];
TSharedPtr< SWidget > Content;
if ( InArgs._AllowCollapsing )
{
Content = SAssignNew(CollectionsExpandableAreaPtr, SExpandableArea)
.MaxHeight(200)
.BorderImage( FEditorStyle::GetBrush("NoBorder") )
.HeaderPadding( FMargin(4.0f, 0.0f, 0.0f, 0.0f) )
.HeaderContent()
[
SNew(SBox)
.Padding(FMargin(6.0f, 0.0f, 0.0f, 0.0f))
[
HeaderContent
]
]
.BodyContent()
[
BodyContent
];
}
else
{
Content = SNew( SVerticalBox )
+SVerticalBox::Slot()
.AutoHeight()
[
HeaderContent
]
+SVerticalBox::Slot()
[
BodyContent
];
}
ChildSlot
[
SNew(SOverlay)
// Main content
+SOverlay::Slot()
[
Content.ToSharedRef()
]
// Drop target overlay
+SOverlay::Slot()
[
SNew(SBorder)
.Padding(0)
.Visibility(EVisibility::HitTestInvisible)
.BorderImage(this, &SCollectionView::GetCollectionViewDropTargetBorder)
.BorderBackgroundColor(FLinearColor::Yellow)
[
SNullWidget::NullWidget
]
]
];
UpdateCollectionItems();
}
void SCollectionView::HandleCollectionCreated( const FCollectionNameType& Collection )
{
UpdateCollectionItems();
}
void SCollectionView::HandleCollectionRenamed( const FCollectionNameType& OriginalCollection, const FCollectionNameType& NewCollection )
{
// If the original collection was expanded, we want to pass that expansion state onto its new entry
bool bWasExpanded = false;
{
TSharedPtr<FCollectionItem> OriginalCollectionItem = AvailableCollections.FindRef(OriginalCollection);
if (OriginalCollectionItem.IsValid())
{
bWasExpanded = CollectionTreePtr->IsItemExpanded(OriginalCollectionItem);
}
}
UpdateCollectionItems();
if (bWasExpanded)
{
TSharedPtr<FCollectionItem> NewCollectionItem = AvailableCollections.FindRef(NewCollection);
if (NewCollectionItem.IsValid())
{
CollectionTreePtr->SetItemExpansion(NewCollectionItem, true);
}
}
}
void SCollectionView::HandleCollectionReparented( const FCollectionNameType& Collection, const TOptional<FCollectionNameType>& OldParent, const TOptional<FCollectionNameType>& NewParent )
{
UpdateCollectionItems();
}
void SCollectionView::HandleCollectionDestroyed( const FCollectionNameType& Collection )
{
UpdateCollectionItems();
}
void SCollectionView::UpdateCollectionItems()
{
struct FGatherCollectionItems
{
FGatherCollectionItems()
: CollectionManagerModule(FCollectionManagerModule::GetModule())
{
}
void GatherCollectionItems(FAvailableCollectionsMap& OutAvailableCollections)
{
OutAvailableCollections.Reset();
TArray<FCollectionNameType> RootCollections;
CollectionManagerModule.Get().GetRootCollections(RootCollections);
ProcessGatheredCollectionsAndRecurse(RootCollections, nullptr, OutAvailableCollections);
}
void GatherChildCollectionItems(const TSharedPtr<FCollectionItem>& InParentCollectionItem, FAvailableCollectionsMap& OutAvailableCollections)
{
TArray<FCollectionNameType> ChildCollections;
CollectionManagerModule.Get().GetChildCollections(InParentCollectionItem->CollectionName, InParentCollectionItem->CollectionType, ChildCollections);
ProcessGatheredCollectionsAndRecurse(ChildCollections, InParentCollectionItem, OutAvailableCollections);
}
void ProcessGatheredCollectionsAndRecurse(const TArray<FCollectionNameType>& InCollections, const TSharedPtr<FCollectionItem>& InParentCollectionItem, FAvailableCollectionsMap& OutAvailableCollections)
{
for (const FCollectionNameType& Collection : InCollections)
{
// Never display system collections
if (Collection.Type == ECollectionShareType::CST_System)
{
continue;
}
TSharedPtr<FCollectionItem> CollectionItem = MakeShareable(new FCollectionItem(Collection.Name, Collection.Type));
OutAvailableCollections.Add(Collection, CollectionItem);
if (InParentCollectionItem.IsValid())
{
// Fixup the parent and child pointers
InParentCollectionItem->ChildCollections.Add(CollectionItem);
CollectionItem->ParentCollection = InParentCollectionItem;
}
// Recurse
GatherChildCollectionItems(CollectionItem, OutAvailableCollections);
}
}
FCollectionManagerModule& CollectionManagerModule;
};
// Backup the current selection and expansion state of our collections
// We're about to re-create the tree, so we'll need to re-apply this again afterwards
TArray<FCollectionNameType> SelectedCollections;
TArray<FCollectionNameType> ExpandedCollections;
{
const auto SelectedCollectionItems = CollectionTreePtr->GetSelectedItems();
SelectedCollections.Reserve(SelectedCollectionItems.Num());
for (const TSharedPtr<FCollectionItem>& SelectedCollectionItem : SelectedCollectionItems)
{
SelectedCollections.Add(FCollectionNameType(SelectedCollectionItem->CollectionName, SelectedCollectionItem->CollectionType));
}
}
{
TSet<TSharedPtr<FCollectionItem>> ExpandedCollectionItems;
CollectionTreePtr->GetExpandedItems(ExpandedCollectionItems);
ExpandedCollections.Reserve(ExpandedCollectionItems.Num());
for (const TSharedPtr<FCollectionItem>& ExpandedCollectionItem : ExpandedCollectionItems)
{
ExpandedCollections.Add(FCollectionNameType(ExpandedCollectionItem->CollectionName, ExpandedCollectionItem->CollectionType));
}
}
FGatherCollectionItems GatherCollectionItems;
GatherCollectionItems.GatherCollectionItems(AvailableCollections);
UpdateFilteredCollectionItems();
// Restore selection and expansion
SetSelectedCollections(SelectedCollections, false);
SetExpandedCollections(ExpandedCollections);
}
void SCollectionView::UpdateFilteredCollectionItems()
{
VisibleCollections.Reset();
VisibleRootCollectionItems.Reset();
auto AddVisibleCollection = [&](const TSharedPtr<FCollectionItem>& InCollectionItem)
{
VisibleCollections.Add(FCollectionNameType(InCollectionItem->CollectionName, InCollectionItem->CollectionType));
if (!InCollectionItem->ParentCollection.IsValid())
{
VisibleRootCollectionItems.AddUnique(InCollectionItem);
}
};
auto AddVisibleCollectionRecursive = [&](const TSharedPtr<FCollectionItem>& InCollectionItem)
{
TSharedPtr<FCollectionItem> CollectionItemToAdd = InCollectionItem;
do
{
AddVisibleCollection(CollectionItemToAdd);
CollectionItemToAdd = CollectionItemToAdd->ParentCollection.Pin();
}
while(CollectionItemToAdd.IsValid());
};
// Do we have an active filter to test against?
if (CollectionItemTextFilter->GetRawFilterText().IsEmpty())
{
// No filter, just mark everything as visible
for (const auto& AvailableCollectionInfo : AvailableCollections)
{
AddVisibleCollection(AvailableCollectionInfo.Value);
}
}
else
{
TArray<TSharedRef<FCollectionItem>> CollectionsToExpandTo;
// Test everything against the filter - a visible child needs to make sure its parents are also marked as visible
for (const auto& AvailableCollectionInfo : AvailableCollections)
{
const TSharedPtr<FCollectionItem>& CollectionItem = AvailableCollectionInfo.Value;
if (CollectionItemTextFilter->PassesFilter(*CollectionItem))
{
AddVisibleCollectionRecursive(CollectionItem);
CollectionsToExpandTo.Add(CollectionItem.ToSharedRef());
}
}
// Make sure all matching items have their parents expanded so they can be seen
for (const TSharedRef<FCollectionItem>& CollectionItem : CollectionsToExpandTo)
{
ExpandParentItems(CollectionItem);
}
}
VisibleRootCollectionItems.Sort(FCollectionItem::FCompareFCollectionItemByName());
CollectionTreePtr->RequestTreeRefresh();
}
void SCollectionView::SetCollectionsSearchFilterText( const FText& InSearchText )
{
CollectionItemTextFilter->SetRawFilterText( InSearchText );
SearchBoxPtr->SetError( CollectionItemTextFilter->GetFilterErrorText() );
}
FText SCollectionView::GetCollectionsSearchFilterText() const
{
return CollectionItemTextFilter->GetRawFilterText();
}
void SCollectionView::SetSelectedCollections(const TArray<FCollectionNameType>& CollectionsToSelect, const bool bEnsureVisible)
{
// Prevent the selection changed delegate since the invoking code requested it
FScopedPreventSelectionChangedDelegate DelegatePrevention( SharedThis(this) );
// Expand the collections area if we are indeed selecting at least one collection
if ( bEnsureVisible && CollectionsToSelect.Num() > 0 && CollectionsExpandableAreaPtr.IsValid() )
{
CollectionsExpandableAreaPtr->SetExpanded(true);
}
// Clear the selection to start, then add the selected items as they are found
CollectionTreePtr->ClearSelection();
for (const FCollectionNameType& CollectionToSelect : CollectionsToSelect)
{
TSharedPtr<FCollectionItem> CollectionItemToSelect = AvailableCollections.FindRef(CollectionToSelect);
if (CollectionItemToSelect.IsValid())
{
if (bEnsureVisible)
{
ExpandParentItems(CollectionItemToSelect.ToSharedRef());
CollectionTreePtr->RequestScrollIntoView(CollectionItemToSelect);
}
CollectionTreePtr->SetItemSelection(CollectionItemToSelect, true);
// If the selected collection doesn't pass our current filter, we need to clear it
if (bEnsureVisible && !CollectionItemTextFilter->PassesFilter(FCollectionItem(CollectionItemToSelect->CollectionName, CollectionItemToSelect->CollectionType)))
{
SearchBoxPtr->SetText(FText::GetEmpty());
}
}
}
}
void SCollectionView::SetExpandedCollections(const TArray<FCollectionNameType>& CollectionsToExpand)
{
// Clear the expansion to start, then add the expanded items as they are found
CollectionTreePtr->ClearExpandedItems();
for (const FCollectionNameType& CollectionToExpand : CollectionsToExpand)
{
TSharedPtr<FCollectionItem> CollectionItemToExpand = AvailableCollections.FindRef(CollectionToExpand);
if (CollectionItemToExpand.IsValid())
{
CollectionTreePtr->SetItemExpansion(CollectionItemToExpand, true);
}
}
}
void SCollectionView::ClearSelection()
{
// Prevent the selection changed delegate since the invoking code requested it
FScopedPreventSelectionChangedDelegate DelegatePrevention( SharedThis(this) );
// Clear the selection to start, then add the selected paths as they are found
CollectionTreePtr->ClearSelection();
}
TArray<FCollectionNameType> SCollectionView::GetSelectedCollections() const
{
TArray<FCollectionNameType> RetArray;
TArray<TSharedPtr<FCollectionItem>> Items = CollectionTreePtr->GetSelectedItems();
for ( int32 ItemIdx = 0; ItemIdx < Items.Num(); ++ItemIdx )
{
const TSharedPtr<FCollectionItem>& Item = Items[ItemIdx];
RetArray.Add(FCollectionNameType(Item->CollectionName, Item->CollectionType));
}
return RetArray;
}
void SCollectionView::SetSelectedAssets(const TArray<FAssetData>& SelectedAssets)
{
if ( QuickAssetManagement.IsValid() )
{
QuickAssetManagement->SetCurrentAssets(SelectedAssets);
}
}
void SCollectionView::ApplyHistoryData( const FHistoryData& History )
{
// Prevent the selection changed delegate because it would add more history when we are just setting a state
FScopedPreventSelectionChangedDelegate DelegatePrevention( SharedThis(this) );
CollectionTreePtr->ClearSelection();
for ( auto HistoryIt = History.SourcesData.Collections.CreateConstIterator(); HistoryIt; ++HistoryIt)
{
TSharedPtr<FCollectionItem> CollectionHistoryItem = AvailableCollections.FindRef(FCollectionNameType((*HistoryIt).Name, (*HistoryIt).Type));
if (CollectionHistoryItem.IsValid())
{
ExpandParentItems(CollectionHistoryItem.ToSharedRef());
CollectionTreePtr->RequestScrollIntoView(CollectionHistoryItem);
CollectionTreePtr->SetItemSelection(CollectionHistoryItem, true);
}
}
}
void SCollectionView::SaveSettings(const FString& IniFilename, const FString& IniSection, const FString& SettingsString) const
{
auto SaveCollectionsArrayToIni = [&](const FString& InSubKey, const TArray<TSharedPtr<FCollectionItem>>& InCollectionItems)
{
FString CollectionsString;
for (const TSharedPtr<FCollectionItem>& CollectionItem : InCollectionItems)
{
if (CollectionsString.Len() > 0)
{
CollectionsString += TEXT(",");
}
CollectionsString += CollectionItem->CollectionName.ToString();
CollectionsString += TEXT("?");
CollectionsString += FString::FromInt(CollectionItem->CollectionType);
}
GConfig->SetString(*IniSection, *(SettingsString + InSubKey), *CollectionsString, IniFilename);
};
const bool IsCollectionsExpanded = CollectionsExpandableAreaPtr.IsValid() ? CollectionsExpandableAreaPtr->IsExpanded() : true;
GConfig->SetBool(*IniSection, *(SettingsString + TEXT(".CollectionsExpanded")), IsCollectionsExpanded, IniFilename);
SaveCollectionsArrayToIni(TEXT(".SelectedCollections"), CollectionTreePtr->GetSelectedItems());
{
TSet<TSharedPtr<FCollectionItem>> ExpandedCollectionItems;
CollectionTreePtr->GetExpandedItems(ExpandedCollectionItems);
SaveCollectionsArrayToIni(TEXT(".ExpandedCollections"), ExpandedCollectionItems.Array());
}
}
void SCollectionView::LoadSettings(const FString& IniFilename, const FString& IniSection, const FString& SettingsString)
{
auto LoadCollectionsArrayFromIni = [&](const FString& InSubKey) -> TArray<FCollectionNameType>
{
TArray<FCollectionNameType> RetCollectionsArray;
FString CollectionsArrayString;
if (GConfig->GetString(*IniSection, *(SettingsString + InSubKey), CollectionsArrayString, IniFilename))
{
TArray<FString> CollectionStrings;
CollectionsArrayString.ParseIntoArray(CollectionStrings, TEXT(","), /*bCullEmpty*/true);
for (const FString& CollectionString : CollectionStrings)
{
FString CollectionName;
FString CollectionTypeString;
if (CollectionString.Split(TEXT("?"), &CollectionName, &CollectionTypeString))
{
const int32 CollectionType = FCString::Atoi(*CollectionTypeString);
if (CollectionType >= 0 && CollectionType < ECollectionShareType::CST_All)
{
RetCollectionsArray.Add(FCollectionNameType(FName(*CollectionName), ECollectionShareType::Type(CollectionType)));
}
}
}
}
return RetCollectionsArray;
};
// Collection expansion state
bool bCollectionsExpanded = false;
if (CollectionsExpandableAreaPtr.IsValid() && GConfig->GetBool(*IniSection, *(SettingsString + TEXT(".CollectionsExpanded")), bCollectionsExpanded, IniFilename))
{
CollectionsExpandableAreaPtr->SetExpanded(bCollectionsExpanded);
}
// Selected Collections
TArray<FCollectionNameType> NewSelectedCollections = LoadCollectionsArrayFromIni(TEXT(".SelectedCollections"));
if (NewSelectedCollections.Num() > 0)
{
SetSelectedCollections(NewSelectedCollections);
const TArray<TSharedPtr<FCollectionItem>> SelectedCollectionItems = CollectionTreePtr->GetSelectedItems();
if (SelectedCollectionItems.Num() > 0)
{
CollectionSelectionChanged(SelectedCollectionItems[0], ESelectInfo::Direct);
}
}
// Expanded Collections
TArray<FCollectionNameType> NewExpandedCollections = LoadCollectionsArrayFromIni(TEXT(".ExpandedCollections"));
if (NewExpandedCollections.Num() > 0)
{
SetExpandedCollections(NewExpandedCollections);
}
}
FReply SCollectionView::OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent )
{
if( Commands->ProcessCommandBindings( InKeyEvent ) )
{
return FReply::Handled();
}
return FReply::Unhandled();
}
void SCollectionView::OnDragEnter( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent )
{
ValidateDragDropOnCollectionTree(MyGeometry, DragDropEvent, bDraggedOver); // updates bDraggedOver
}
void SCollectionView::OnDragLeave( const FDragDropEvent& DragDropEvent )
{
bDraggedOver = false;
}
FReply SCollectionView::OnDragOver( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent )
{
ValidateDragDropOnCollectionTree(MyGeometry, DragDropEvent, bDraggedOver); // updates bDraggedOver
return (bDraggedOver) ? FReply::Handled() : FReply::Unhandled();
}
FReply SCollectionView::OnDrop( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent )
{
if (ValidateDragDropOnCollectionTree(MyGeometry, DragDropEvent, bDraggedOver)) // updates bDraggedOver
{
bDraggedOver = false;
return HandleDragDropOnCollectionTree(MyGeometry, DragDropEvent);
}
if (bDraggedOver)
{
// We were able to handle this operation, but could not due to another error - still report this drop as handled so it doesn't fall through to other widgets
bDraggedOver = false;
return FReply::Handled();
}
return FReply::Unhandled();
}
bool SCollectionView::ShouldAllowSelectionChangedDelegate() const
{
return PreventSelectionChangedDelegateCount == 0;
}
FReply SCollectionView::MakeAddCollectionMenu()
{
// Get all menu extenders for this context menu from the content browser module
FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>( TEXT("ContentBrowser") );
TArray<FContentBrowserMenuExtender> MenuExtenderDelegates = ContentBrowserModule.GetAllCollectionViewContextMenuExtenders();
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, true);
CollectionContextMenu->UpdateProjectSourceControl();
CollectionContextMenu->MakeNewCollectionSubMenu(MenuBuilder);
FSlateApplication::Get().PushMenu(
AsShared(),
FWidgetPath(),
MenuBuilder.MakeWidget(),
FSlateApplication::Get().GetCursorPos(),
FPopupTransitionEffect( FPopupTransitionEffect::TopMenu )
);
return FReply::Handled();
}
EVisibility SCollectionView::GetCollectionsTitleTextVisibility() const
{
// Only show the title text if we have an expansion area, but are collapsed
return (CollectionsExpandableAreaPtr.IsValid() && !CollectionsExpandableAreaPtr->IsExpanded()) ? EVisibility::Visible : EVisibility::Collapsed;
}
EVisibility SCollectionView::GetCollectionsSearchBoxVisibility() const
{
// Only show the search box if we have an expanded expansion area, or aren't currently using an expansion area
return (!CollectionsExpandableAreaPtr.IsValid() || CollectionsExpandableAreaPtr->IsExpanded()) ? EVisibility::Visible : EVisibility::Collapsed;
}
EVisibility SCollectionView::GetAddCollectionButtonVisibility() const
{
return (bAllowCollectionButtons && ( !CollectionsExpandableAreaPtr.IsValid() || CollectionsExpandableAreaPtr->IsExpanded() ) ) ? EVisibility::Visible : EVisibility::Collapsed;
}
void SCollectionView::CreateCollectionItem( ECollectionShareType::Type CollectionType )
{
if ( ensure(CollectionType != ECollectionShareType::CST_All) )
{
FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
const FName BaseCollectionName = *LOCTEXT("NewCollectionName", "NewCollection").ToString();
FName CollectionName;
CollectionManagerModule.Get().CreateUniqueCollectionName(BaseCollectionName, CollectionType, CollectionName);
TSharedPtr<FCollectionItem> NewItem = MakeShareable(new FCollectionItem(CollectionName, CollectionType));
// Adding a new collection now, so clear any filter we may have applied
SearchBoxPtr->SetText(FText::GetEmpty());
// Mark the new collection for rename and that it is new so it will be created upon successful rename
NewItem->bRenaming = true;
NewItem->bNewCollection = true;
AvailableCollections.Add( FCollectionNameType(NewItem->CollectionName, NewItem->CollectionType), NewItem );
UpdateFilteredCollectionItems();
CollectionTreePtr->RequestScrollIntoView(NewItem);
CollectionTreePtr->SetSelection( NewItem );
}
}
void SCollectionView::RenameCollectionItem( const TSharedPtr<FCollectionItem>& ItemToRename )
{
if ( ensure(ItemToRename.IsValid()) )
{
ItemToRename->bRenaming = true;
CollectionTreePtr->RequestScrollIntoView(ItemToRename);
}
}
void SCollectionView::DeleteCollectionItems( const TArray<TSharedPtr<FCollectionItem>>& ItemsToDelete )
{
if (ItemsToDelete.Num() == 0)
{
return;
}
// Before we delete anything (as this will trigger a tree update) we need to work out what our new selection should be in the case that
// all of the selected items are removed
const TArray<TSharedPtr<FCollectionItem>> PreviouslySelectedItems = CollectionTreePtr->GetSelectedItems();
// Get the first selected item that will be deleted so we can find a suitable new selection
TSharedPtr<FCollectionItem> FirstSelectedItemDeleted;
for (const auto& ItemToDelete : ItemsToDelete)
{
if (PreviouslySelectedItems.Contains(ItemToDelete))
{
FirstSelectedItemDeleted = ItemToDelete;
break;
}
}
// Build up an array of potential new selections (in the case that we're deleting everything that's selected)
// Earlier items should be considered first, we base this list on the first selected item that will be deleted, and include previous siblings, and then all parents and roots
TArray<FCollectionNameType> PotentialNewSelections;
if (FirstSelectedItemDeleted.IsValid())
{
TSharedPtr<FCollectionItem> RootSelectedItemDeleted = FirstSelectedItemDeleted;
TSharedPtr<FCollectionItem> ParentCollectionItem = FirstSelectedItemDeleted->ParentCollection.Pin();
if (ParentCollectionItem.IsValid())
{
// Add all the siblings until we find the item that will be deleted
for (const auto& ChildItemWeakPtr : ParentCollectionItem->ChildCollections)
{
TSharedPtr<FCollectionItem> ChildItem = ChildItemWeakPtr.Pin();
if (ChildItem.IsValid())
{
if (ChildItem == FirstSelectedItemDeleted)
{
break;
}
// We add siblings as the start, as the closest sibling should be the first match
PotentialNewSelections.Insert(FCollectionNameType(ChildItem->CollectionName, ChildItem->CollectionType), 0);
}
}
// Now add this parent, and all other parents too
do
{
PotentialNewSelections.Add(FCollectionNameType(ParentCollectionItem->CollectionName, ParentCollectionItem->CollectionType));
RootSelectedItemDeleted = ParentCollectionItem;
ParentCollectionItem = ParentCollectionItem->ParentCollection.Pin();
}
while (ParentCollectionItem.IsValid());
}
if (RootSelectedItemDeleted.IsValid())
{
// Add all the root level items before this one
const int32 InsertionPoint = PotentialNewSelections.Num();
for (const auto& RootItem : VisibleRootCollectionItems)
{
if (RootItem == RootSelectedItemDeleted)
{
break;
}
// Add each root item at the insertion point, as the closest item should be a better match
PotentialNewSelections.Insert(FCollectionNameType(RootItem->CollectionName, RootItem->CollectionType), InsertionPoint);
}
}
}
// Delete all given collections
int32 NumSelectedItemsDeleted = 0;
FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
for (const TSharedPtr<FCollectionItem>& ItemToDelete : ItemsToDelete)
{
if (CollectionManagerModule.Get().DestroyCollection(ItemToDelete->CollectionName, ItemToDelete->CollectionType))
{
if (PreviouslySelectedItems.Contains(ItemToDelete))
{
++NumSelectedItemsDeleted;
}
}
else
{
// Display a warning
const FVector2D& CursorPos = FSlateApplication::Get().GetCursorPos();
FSlateRect MessageAnchor(CursorPos.X, CursorPos.Y, CursorPos.X, CursorPos.Y);
ContentBrowserUtils::DisplayMessage(
FText::Format( LOCTEXT("CollectionDestroyFailed", "Failed to destroy collection. {0}"), CollectionManagerModule.Get().GetLastError() ),
MessageAnchor,
CollectionTreePtr.ToSharedRef()
);
}
}
// DestroyCollection will have triggered a notification that will have updated the tree, we now need to apply a suitable selection...
// Did this delete change the list of selected items?
if (NumSelectedItemsDeleted > 0 || PreviouslySelectedItems.Num() == 0)
{
// If we removed everything that was selected, we need to try and find a suitable replacement...
if (NumSelectedItemsDeleted >= PreviouslySelectedItems.Num() && VisibleCollections.Num() > 1)
{
// Include the first visible item as an absolute last resort should everything else suitable have been removed from the tree
PotentialNewSelections.Add(*VisibleCollections.CreateConstIterator());
// Check the potential new selections array and try and select the first one that's still visible in the tree
TArray<FCollectionNameType> NewItemSelection;
for (const FCollectionNameType& PotentialNewSelection : PotentialNewSelections)
{
if (VisibleCollections.Contains(PotentialNewSelection))
{
NewItemSelection.Add(PotentialNewSelection);
break;
}
}
SetSelectedCollections(NewItemSelection, true);
}
// Broadcast the new selection
const TArray<TSharedPtr<FCollectionItem>> UpdatedSelectedItems = CollectionTreePtr->GetSelectedItems();
CollectionSelectionChanged((UpdatedSelectedItems.Num() > 0) ? UpdatedSelectedItems[0] : nullptr, ESelectInfo::Direct);
}
}
EVisibility SCollectionView::GetCollectionTreeVisibility() const
{
return AvailableCollections.Num() > 0 ? EVisibility::Visible : EVisibility::Collapsed;
}
const FSlateBrush* SCollectionView::GetCollectionViewDropTargetBorder() const
{
return bDraggedOver ? FEditorStyle::GetBrush("ContentBrowser.CollectionTreeDragDropBorder") : FEditorStyle::GetBrush("NoBorder");
}
TSharedRef<ITableRow> SCollectionView::GenerateCollectionRow( TSharedPtr<FCollectionItem> CollectionItem, const TSharedRef<STableViewBase>& OwnerTable )
{
check(CollectionItem.IsValid());
// Only bind the check box callbacks if we're allowed to show check boxes
TAttribute<bool> IsCollectionCheckBoxEnabledAttribute;
TAttribute<ECheckBoxState> IsCollectionCheckedAttribute;
FOnCheckStateChanged OnCollectionCheckStateChangedDelegate;
if ( QuickAssetManagement.IsValid() )
{
IsCollectionCheckBoxEnabledAttribute.Bind(TAttribute<bool>::FGetter::CreateSP(this, &SCollectionView::IsCollectionCheckBoxEnabled, CollectionItem));
IsCollectionCheckedAttribute.Bind(TAttribute<ECheckBoxState>::FGetter::CreateSP(this, &SCollectionView::IsCollectionChecked, CollectionItem));
OnCollectionCheckStateChangedDelegate.BindSP(this, &SCollectionView::OnCollectionCheckStateChanged, CollectionItem);
}
TSharedPtr< STableRow< TSharedPtr<FCollectionItem> > > TableRow = SNew( STableRow< TSharedPtr<FCollectionItem> >, OwnerTable )
.OnDragDetected(this, &SCollectionView::OnCollectionDragDetected);
TableRow->SetContent
(
SNew(SCollectionTreeItem)
.ParentWidget(SharedThis(this))
.CollectionItem(CollectionItem)
.OnNameChangeCommit(this, &SCollectionView::CollectionNameChangeCommit)
.OnVerifyRenameCommit(this, &SCollectionView::CollectionVerifyRenameCommit)
.OnValidateDragDrop(this, &SCollectionView::ValidateDragDropOnCollectionItem)
.OnHandleDragDrop(this, &SCollectionView::HandleDragDropOnCollectionItem)
.IsSelected(TableRow.Get(), &STableRow< TSharedPtr<FCollectionItem> >::IsSelectedExclusively)
.IsReadOnly(this, &SCollectionView::IsCollectionNameReadOnly)
.HighlightText(this, &SCollectionView::GetCollectionsSearchFilterText)
.IsCheckBoxEnabled(IsCollectionCheckBoxEnabledAttribute)
.IsCollectionChecked(IsCollectionCheckedAttribute)
.OnCollectionCheckStateChanged(OnCollectionCheckStateChangedDelegate)
);
return TableRow.ToSharedRef();
}
void SCollectionView::GetCollectionItemChildren( TSharedPtr<FCollectionItem> InParentItem, TArray< TSharedPtr<FCollectionItem> >& OutChildItems ) const
{
for (const auto& ChildItemWeakPtr : InParentItem->ChildCollections)
{
TSharedPtr<FCollectionItem> ChildItem = ChildItemWeakPtr.Pin();
if (ChildItem.IsValid() && VisibleCollections.Contains(FCollectionNameType(ChildItem->CollectionName, ChildItem->CollectionType)))
{
OutChildItems.Add(ChildItem);
}
}
OutChildItems.Sort(FCollectionItem::FCompareFCollectionItemByName());
}
FReply SCollectionView::OnCollectionDragDetected(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
{
if (bAllowCollectionDrag && MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton))
{
const TArray<FCollectionNameType> SelectedCollections = GetSelectedCollections();
if (SelectedCollections.Num() > 0)
{
TSharedRef<FCollectionDragDropOp> DragDropOp = FCollectionDragDropOp::New(SelectedCollections);
CurrentCollectionDragDropOp = DragDropOp;
return FReply::Handled().BeginDragDrop(DragDropOp);
}
}
return FReply::Unhandled();
}
bool SCollectionView::ValidateDragDropOnCollectionTree(const FGeometry& Geometry, const FDragDropEvent& DragDropEvent, bool& OutIsKnownDragOperation)
{
OutIsKnownDragOperation = false;
TSharedPtr<FDragDropOperation> Operation = DragDropEvent.GetOperation();
if (!Operation.IsValid())
{
return false;
}
if (Operation->IsOfType<FCollectionDragDropOp>())
{
OutIsKnownDragOperation = true;
return true;
}
return false;
}
FReply SCollectionView::HandleDragDropOnCollectionTree(const FGeometry& Geometry, const FDragDropEvent& DragDropEvent)
{
// Should have already called ValidateDragDropOnCollectionTree prior to calling this...
TSharedPtr<FDragDropOperation> Operation = DragDropEvent.GetOperation();
check(Operation.IsValid());
FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
if (Operation->IsOfType<FCollectionDragDropOp>())
{
TSharedPtr<FCollectionDragDropOp> DragDropOp = StaticCastSharedPtr<FCollectionDragDropOp>(Operation);
// Reparent all of the collections in the drag drop so that they are root level items
for (const FCollectionNameType& NewChildCollection : DragDropOp->Collections)
{
if (!CollectionManagerModule.Get().ReparentCollection(
NewChildCollection.Name, NewChildCollection.Type,
NAME_None, ECollectionShareType::CST_All
))
{
ContentBrowserUtils::DisplayMessage(CollectionManagerModule.Get().GetLastError(), Geometry.GetClippingRect(), SharedThis(this));
}
}
return FReply::Handled();
}
return FReply::Unhandled();
}
bool SCollectionView::ValidateDragDropOnCollectionItem(TSharedRef<FCollectionItem> CollectionItem, const FGeometry& Geometry, const FDragDropEvent& DragDropEvent, bool& OutIsKnownDragOperation)
{
OutIsKnownDragOperation = false;
TSharedPtr<FDragDropOperation> Operation = DragDropEvent.GetOperation();
if (!Operation.IsValid())
{
return false;
}
bool bIsValidDrag = false;
TOptional<EMouseCursor::Type> NewDragCursor;
if (Operation->IsOfType<FCollectionDragDropOp>())
{
TSharedPtr<FCollectionDragDropOp> DragDropOp = StaticCastSharedPtr<FCollectionDragDropOp>(Operation);
OutIsKnownDragOperation = true;
FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
bIsValidDrag = true;
for (const FCollectionNameType& PotentialChildCollection : DragDropOp->Collections)
{
bIsValidDrag = CollectionManagerModule.Get().IsValidParentCollection(
PotentialChildCollection.Name, PotentialChildCollection.Type,
CollectionItem->CollectionName, CollectionItem->CollectionType
);
if (!bIsValidDrag)
{
DragDropOp->SetToolTip(CollectionManagerModule.Get().GetLastError(), FEditorStyle::GetBrush(TEXT("Graph.ConnectorFeedback.Error")));
break;
}
}
// If we are dragging over a child collection item, then this view as a whole should not be marked as dragged over
bDraggedOver = false;
}
else if (Operation->IsOfType<FAssetDragDropOp>())
{
TSharedPtr<FAssetDragDropOp> DragDropOp = StaticCastSharedPtr<FAssetDragDropOp>(Operation);
OutIsKnownDragOperation = true;
bIsValidDrag = DragDropOp->AssetData.Num() > 0;
}
// Set the default slashed circle if this drag is invalid and a drag operation hasn't set NewDragCursor to something custom
if (!bIsValidDrag && !NewDragCursor.IsSet())
{
NewDragCursor = EMouseCursor::SlashedCircle;
}
Operation->SetCursorOverride(NewDragCursor);
return bIsValidDrag;
}
FReply SCollectionView::HandleDragDropOnCollectionItem(TSharedRef<FCollectionItem> CollectionItem, const FGeometry& Geometry, const FDragDropEvent& DragDropEvent)
{
// Should have already called ValidateDragDropOnCollectionItem prior to calling this...
TSharedPtr<FDragDropOperation> Operation = DragDropEvent.GetOperation();
check(Operation.IsValid());
FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
if (Operation->IsOfType<FCollectionDragDropOp>())
{
TSharedPtr<FCollectionDragDropOp> DragDropOp = StaticCastSharedPtr<FCollectionDragDropOp>(Operation);
// Make sure our drop item is marked as expanded so that we'll be able to see the newly added children
CollectionTreePtr->SetItemExpansion(CollectionItem, true);
// Reparent all of the collections in the drag drop so that they are our immediate children
for (const FCollectionNameType& NewChildCollection : DragDropOp->Collections)
{
if (!CollectionManagerModule.Get().ReparentCollection(
NewChildCollection.Name, NewChildCollection.Type,
CollectionItem->CollectionName, CollectionItem->CollectionType
))
{
ContentBrowserUtils::DisplayMessage(CollectionManagerModule.Get().GetLastError(), Geometry.GetClippingRect(), SharedThis(this));
}
}
return FReply::Handled();
}
else if (Operation->IsOfType<FAssetDragDropOp>())
{
TSharedPtr<FAssetDragDropOp> DragDropOp = StaticCastSharedPtr<FAssetDragDropOp>(Operation);
TArray<FName> ObjectPaths;
ObjectPaths.Reserve(DragDropOp->AssetData.Num());
for (const FAssetData& AssetData : DragDropOp->AssetData)
{
ObjectPaths.Add(AssetData.ObjectPath);
}
int32 NumAdded = 0;
FText Message;
if (CollectionManagerModule.Get().AddToCollection(CollectionItem->CollectionName, CollectionItem->CollectionType, ObjectPaths, &NumAdded))
{
if (DragDropOp->AssetData.Num() == 1)
{
FFormatNamedArguments Args;
Args.Add(TEXT("AssetName"), FText::FromName(DragDropOp->AssetData[0].AssetName));
Args.Add(TEXT("CollectionName"), FText::FromName(CollectionItem->CollectionName));
Message = FText::Format(LOCTEXT("CollectionAssetsAdded", "Added {AssetName} to {CollectionName}"), Args);
}
else
{
FFormatNamedArguments Args;
Args.Add(TEXT("Number"), NumAdded);
Args.Add(TEXT("CollectionName"), FText::FromName(CollectionItem->CollectionName));
Message = FText::Format(LOCTEXT("CollectionAssetsAdded", "Added {Number} asset(s) to {CollectionName}"), Args);
}
}
else
{
Message = CollectionManagerModule.Get().GetLastError();
}
// Added items to the collection or failed. Either way, display the message.
ContentBrowserUtils::DisplayMessage(Message, Geometry.GetClippingRect(), SharedThis(this));
return FReply::Handled();
}
return FReply::Unhandled();
}
void SCollectionView::ExpandParentItems(const TSharedRef<FCollectionItem>& InCollectionItem)
{
for (TSharedPtr<FCollectionItem> CollectionItemToExpand = InCollectionItem->ParentCollection.Pin();
CollectionItemToExpand.IsValid();
CollectionItemToExpand = CollectionItemToExpand->ParentCollection.Pin()
)
{
CollectionTreePtr->SetItemExpansion(CollectionItemToExpand, true);
}
}
TSharedPtr<SWidget> SCollectionView::MakeCollectionTreeContextMenu()
{
if ( !bAllowRightClickMenu )
{
return NULL;
}
return CollectionContextMenu->MakeCollectionTreeContextMenu(Commands);
}
bool SCollectionView::IsCollectionCheckBoxEnabled( TSharedPtr<FCollectionItem> CollectionItem ) const
{
return QuickAssetManagement.IsValid() && QuickAssetManagement->IsCollectionEnabled(FCollectionNameType(CollectionItem->CollectionName, CollectionItem->CollectionType));
}
ECheckBoxState SCollectionView::IsCollectionChecked( TSharedPtr<FCollectionItem> CollectionItem ) const
{
if ( QuickAssetManagement.IsValid() )
{
return QuickAssetManagement->GetCollectionCheckState(FCollectionNameType(CollectionItem->CollectionName, CollectionItem->CollectionType));
}
return ECheckBoxState::Unchecked;
}
void SCollectionView::OnCollectionCheckStateChanged( ECheckBoxState NewState, TSharedPtr<FCollectionItem> CollectionItem )
{
if ( QuickAssetManagement.IsValid() )
{
switch(NewState)
{
case ECheckBoxState::Checked:
QuickAssetManagement->AddCurrentAssetsToCollection(FCollectionNameType(CollectionItem->CollectionName, CollectionItem->CollectionType));
break;
case ECheckBoxState::Unchecked:
QuickAssetManagement->RemoveCurrentAssetsFromCollection(FCollectionNameType(CollectionItem->CollectionName, CollectionItem->CollectionType));
break;
default:
break;
}
}
}
void SCollectionView::CollectionSelectionChanged( TSharedPtr< FCollectionItem > CollectionItem, ESelectInfo::Type /*SelectInfo*/ )
{
if ( ShouldAllowSelectionChangedDelegate() && OnCollectionSelected.IsBound() )
{
if ( CollectionItem.IsValid() )
{
OnCollectionSelected.Execute(FCollectionNameType(CollectionItem->CollectionName, CollectionItem->CollectionType));
}
else
{
OnCollectionSelected.Execute(FCollectionNameType(NAME_None, ECollectionShareType::CST_All));
}
}
}
void SCollectionView::CollectionItemScrolledIntoView( TSharedPtr<FCollectionItem> CollectionItem, const TSharedPtr<ITableRow>& Widget )
{
if ( CollectionItem->bRenaming && Widget.IsValid() && Widget->GetContent().IsValid() )
{
CollectionItem->OnRenamedRequestEvent.Broadcast();
}
}
bool SCollectionView::IsCollectionNameReadOnly() const
{
// We can't rename collections while they're being dragged
TSharedPtr<FCollectionDragDropOp> DragDropOp = CurrentCollectionDragDropOp.Pin();
if (DragDropOp.IsValid())
{
TArray<TSharedPtr<FCollectionItem>> SelectedCollectionItems = CollectionTreePtr->GetSelectedItems();
for (const auto& SelectedCollectionItem : SelectedCollectionItems)
{
if (DragDropOp->Collections.Contains(FCollectionNameType(SelectedCollectionItem->CollectionName, SelectedCollectionItem->CollectionType)))
{
return true;
}
}
}
CollectionContextMenu->UpdateProjectSourceControl();
return !CollectionContextMenu->CanRenameSelectedCollections();
}
bool SCollectionView::CollectionNameChangeCommit( const TSharedPtr< FCollectionItem >& CollectionItem, const FString& NewName, bool bChangeConfirmed, FText& OutWarningMessage )
{
// There should only ever be one item selected when renaming
check(CollectionTreePtr->GetNumItemsSelected() == 1);
FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
ECollectionShareType::Type CollectionType = CollectionItem->CollectionType;
// If new name is empty, set it back to the original name
const FName NewNameFinal( NewName.IsEmpty() ? CollectionItem->CollectionName : FName(*NewName) );
if ( CollectionItem->bNewCollection )
{
CollectionItem->bNewCollection = false;
// If we can canceled the name change when creating a new asset, we want to silently remove it
if ( !bChangeConfirmed )
{
AvailableCollections.Remove(FCollectionNameType(CollectionItem->CollectionName, CollectionItem->CollectionType));
UpdateFilteredCollectionItems();
return false;
}
if ( !CollectionManagerModule.Get().CreateCollection(NewNameFinal, CollectionType) )
{
// Failed to add the collection, remove it from the list
AvailableCollections.Remove(FCollectionNameType(CollectionItem->CollectionName, CollectionItem->CollectionType));
UpdateFilteredCollectionItems();
OutWarningMessage = FText::Format( LOCTEXT("CreateCollectionFailed", "Failed to create the collection. {0}"), CollectionManagerModule.Get().GetLastError());
return false;
}
}
else
{
// If the old name is the same as the new name, just early exit here.
if ( CollectionItem->CollectionName == NewNameFinal )
{
return true;
}
// If the new name doesn't pass our current filter, we need to clear it
if ( !CollectionItemTextFilter->PassesFilter( FCollectionItem(NewNameFinal, CollectionType) ) )
{
SearchBoxPtr->SetText(FText::GetEmpty());
}
// Otherwise perform the rename
if ( !CollectionManagerModule.Get().RenameCollection(CollectionItem->CollectionName, CollectionType, NewNameFinal, CollectionType) )
{
// Failed to rename the collection
OutWarningMessage = FText::Format( LOCTEXT("RenameCollectionFailed", "Failed to rename the collection. {0}"), CollectionManagerModule.Get().GetLastError());
return false;
}
}
// At this point CollectionItem is no longer a member of the CollectionItems list (as the list is repopulated by
// UpdateCollectionItems, which is called by a broadcast from CollectionManagerModule::RenameCollection, above).
// So search again for the item by name and type.
auto NewCollectionItemPtr = AvailableCollections.Find( FCollectionNameType(NewNameFinal, CollectionType) );
// Reselect the path to notify that the selection has changed
{
FScopedPreventSelectionChangedDelegate DelegatePrevention( SharedThis(this) );
CollectionTreePtr->ClearSelection();
}
// Set the selection
if (NewCollectionItemPtr)
{
const auto& NewCollectionItem = *NewCollectionItemPtr;
CollectionTreePtr->RequestScrollIntoView(NewCollectionItem);
CollectionTreePtr->SetItemSelection(NewCollectionItem, true);
}
return true;
}
bool SCollectionView::CollectionVerifyRenameCommit(const TSharedPtr< FCollectionItem >& CollectionItem, const FString& NewName, const FSlateRect& MessageAnchor, FText& OutErrorMessage)
{
const FName NewNameFinal = *NewName;
// If the new name is the same as the old name, consider this to be unchanged, and accept it.
if (CollectionItem->CollectionName == NewNameFinal)
{
return true;
}
FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
if (CollectionManagerModule.Get().CollectionExists(NewNameFinal, ECollectionShareType::CST_All))
{
// This collection already exists, inform the user and continue
OutErrorMessage = FText::Format(LOCTEXT("RenameCollectionAlreadyExists", "A collection already exists with the name '{0}'."), FText::FromName(NewNameFinal));
// Return false to indicate that the user should enter a new name
return false;
}
return true;
}
#undef LOCTEXT_NAMESPACE