123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034 |
- // Copyright 2017-2021 marynate. All Rights Reserved.
- #include "SExtPathView.h"
- #include "ExtContentBrowser.h"
- #include "ExtContentBrowserSingleton.h"
- #include "ExtContentBrowserUtils.h"
- #include "ExtPackageUtils.h"
- #include "ExtPathViewTypes.h"
- #include "ExtSourcesViewWidgets.h"
- #include "DragDropHandler.h"
- #include "Adapters/SourcesData.h"
- #include "Layout/WidgetPath.h"
- #include "Application/SlateApplicationBase.h"
- #include "Framework/Application/SlateApplication.h"
- #include "Widgets/Input/SSearchBox.h"
- #include "Widgets/Layout/SSeparator.h"
- #include "EditorStyleSet.h"
- #include "AssetRegistry/AssetRegistryModule.h"
- #include "IAssetTools.h"
- #include "AssetToolsModule.h"
- #include "DragAndDrop/AssetDragDropOp.h"
- #include "HAL/FileManager.h"
- #include "Misc/ConfigCacheIni.h"
- #if ECB_WIP_HISTORY
- #include "HistoryManager.h"
- #endif
- #define LOCTEXT_NAMESPACE "ExtContentBrowser"
- SExtPathView::~SExtPathView()
- {
- #if ECB_LEGACY
- // Unsubscribe from content path events
- FPackageName::OnContentPathMounted().RemoveAll( this );
- FPackageName::OnContentPathDismounted().RemoveAll( this );
- // Unsubscribe from class events
- if ( bAllowClassesFolder )
- {
- TSharedRef<FNativeClassHierarchy> NativeClassHierarchy = FContentBrowserSingleton::Get().GetNativeClassHierarchy();
- NativeClassHierarchy->OnClassHierarchyUpdated().RemoveAll( this );
- }
- // Unsubscribe from folder population events
- {
- TSharedRef<FEmptyFolderVisibilityManager> EmptyFolderVisibilityManager = FContentBrowserSingleton::Get().GetEmptyFolderVisibilityManager();
- EmptyFolderVisibilityManager->OnFolderPopulated().RemoveAll(this);
- }
- // Load the asset registry module to stop listening for updates
- FAssetRegistryModule* AssetRegistryModule = FModuleManager::GetModulePtr<FAssetRegistryModule>(TEXT("AssetRegistry"));
- if(AssetRegistryModule)
- {
- AssetRegistryModule->Get().OnPathAdded().RemoveAll(this);
- AssetRegistryModule->Get().OnPathRemoved().RemoveAll(this);
- AssetRegistryModule->Get().OnFilesLoaded().RemoveAll(this);
- }
- #endif
- FExtContentBrowserSingleton::GetAssetRegistry().OnRootPathAdded().RemoveAll(this);
- FExtContentBrowserSingleton::GetAssetRegistry().OnRootPathRemoved().RemoveAll(this);
- FExtContentBrowserSingleton::GetAssetRegistry().OnRootPathUpdated().RemoveAll(this);
- FExtContentBrowserSingleton::GetAssetRegistry().OnFolderStartGathering().RemoveAll(this);
- FExtContentBrowserSingleton::GetAssetRegistry().OnFolderFinishGathering().RemoveAll(this);
- SearchBoxFolderFilter->OnChanged().RemoveAll( this );
- }
- void SExtPathView::Construct( const FArguments& InArgs )
- {
- OnPathSelected = InArgs._OnPathSelected;
- bAllowContextMenu = InArgs._AllowContextMenu;
- OnGetFolderContextMenu = InArgs._OnGetFolderContextMenu;
- OnGetPathContextMenuExtender = InArgs._OnGetPathContextMenuExtender;
- bAllowClassesFolder = InArgs._AllowClassesFolder;
- PreventTreeItemChangedDelegateCount = 0;
- TreeTitle = LOCTEXT("AssetTreeTitle", "Asset Tree");
- PluginVersionText = FExtContentBrowserSingleton::GetPluginVersionText();
- if ( InArgs._FocusSearchBoxWhenOpened )
- {
- RegisterActiveTimer( 0.f, FWidgetActiveTimerDelegate::CreateSP( this, &SExtPathView::SetFocusPostConstruct ) );
- }
- // Listen for when view settings are changed
- UExtContentBrowserSettings::OnSettingChanged().AddSP(this, &SExtPathView::HandleSettingChanged);
- //Setup the SearchBox filter
- SearchBoxFolderFilter = MakeShareable( new FolderTextFilter( FolderTextFilter::FItemToStringArray::CreateSP( this, &SExtPathView::PopulateFolderSearchStrings ) ) );
- SearchBoxFolderFilter->OnChanged().AddSP( this, &SExtPathView::FilterUpdated );
- // Listen to find out when new game content paths are mounted or dismounted, so that we can refresh our root set of paths
- FPackageName::OnContentPathMounted().AddSP( this, &SExtPathView::OnContentPathMountedOrDismounted );
- FPackageName::OnContentPathDismounted().AddSP( this, &SExtPathView::OnContentPathMountedOrDismounted );
- #if ECB_LEGACY
- // Listen to find out when the available classes are changed, so that we can refresh our paths
- if ( bAllowClassesFolder )
- {
- TSharedRef<FNativeClassHierarchy> NativeClassHierarchy = FContentBrowserSingleton::Get().GetNativeClassHierarchy();
- NativeClassHierarchy->OnClassHierarchyUpdated().AddSP( this, &SExtPathView::OnClassHierarchyUpdated );
- }
- // Listen to find out when previously empty paths are populated with content
- {
- TSharedRef<FEmptyFolderVisibilityManager> EmptyFolderVisibilityManager = FContentBrowserSingleton::Get().GetEmptyFolderVisibilityManager();
- EmptyFolderVisibilityManager->OnFolderPopulated().AddSP(this, &SExtPathView::OnFolderPopulated);
- }
- #endif
- if (!TreeViewPtr.IsValid())
- {
- SAssignNew(TreeViewPtr, STreeView< TSharedPtr<FTreeItem> >)
- .TreeItemsSource(&TreeRootItems)
- .OnGenerateRow(this, &SExtPathView::GenerateTreeRow)
- .OnItemScrolledIntoView(this, &SExtPathView::TreeItemScrolledIntoView)
- .ItemHeight(18)
- .SelectionMode(InArgs._SelectionMode)
- .OnSelectionChanged(this, &SExtPathView::TreeSelectionChanged)
- .OnExpansionChanged(this, &SExtPathView::TreeExpansionChanged)
- .OnGetChildren(this, &SExtPathView::GetChildrenForTree)
- .OnSetExpansionRecursive(this, &SExtPathView::SetTreeItemExpansionRecursive)
- .OnContextMenuOpening(this, &SExtPathView::MakePathViewContextMenu)
- .ClearSelectionOnClick(false)
- .HighlightParentNodesForSelection(true);
- }
- ChildSlot
- [
- SNew(SVerticalBox)
- // Search >>
- #if ECB_FOLD
- + SVerticalBox::Slot()
- .AutoHeight()
- .Padding(0, 1, 0, 3)
- [
- SNew(SHorizontalBox)
- + SHorizontalBox::Slot()
- .AutoWidth()
- [
- InArgs._SearchContent.Widget
- ]
- + SHorizontalBox::Slot()
- .FillWidth(1.0f)
- [
- SAssignNew(SearchBoxPtr, SSearchBox)
- .Visibility(InArgs._SearchBarVisibility)
- .HintText( LOCTEXT( "AssetTreeSearchBoxHint", "Search Folders" ) )
- .OnTextChanged( this, &SExtPathView::OnAssetTreeSearchBoxChanged )
- .OnTextCommitted( this, &SExtPathView::OnAssetTreeSearchBoxCommitted )
- ]
- ]
- #endif // Search <<
- // Tree Section >>
- #if ECB_FOLD
- #if 0
- // Tree title
- + SVerticalBox::Slot()
- .AutoHeight()
- .Padding(0)
- [
- SNew(SBorder)
- .BorderBackgroundColor(FLinearColor(1.f, 1.f, 1.f, 1.f))
- .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
- .Padding(1.f)
- .Visibility(this, &SExtPathView::GetTreeTitleVisibility)
- ]
- +SVerticalBox::Slot()
- .AutoHeight()
- .Padding(2)
- [
- SNew(SBorder)
- .BorderBackgroundColor(FLinearColor(0.f, 0.f, 0.f, 0.1f))
- .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
- .Padding(4)
- .Visibility(this, &SExtPathView::GetTreeTitleVisibility)
- [
- SNew(SHorizontalBox)
- +SHorizontalBox::Slot()
- .Padding(4, 0, 0, 0)
- [
- SNew(STextBlock)
- .Font( FAppStyle::GetFontStyle("ContentBrowser.SourceTitleFont") )
- //.TextStyle(FAppStyle::Get(), "ContentBrowser.TopBar.Font")
- .Text(this, &SExtPathView::GetTreeTitle)
- //.Visibility(InArgs._ShowTreeTitle ? EVisibility::Visible : EVisibility::Collapsed)
- .Visibility(this, &SExtPathView::GetTreeTitleVisibility)
- ]
- ]
- ]
- + SVerticalBox::Slot()
- .AutoHeight()
- .Padding(0)
- [
- SNew(SBorder)
- .BorderBackgroundColor(FLinearColor(1.f, 1.f, 1.f, 1.f))
- .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
- .Padding(1.f)
- .Visibility(this, &SExtPathView::GetTreeTitleVisibility)
- ]
- #endif
- // Separator
- +SVerticalBox::Slot()
- .AutoHeight()
- .Padding(0, 0, 0, 2)
- [
- SNew(SSeparator)
- .Visibility( ( InArgs._ShowSeparator) ? EVisibility::Visible : EVisibility::Collapsed )
- ]
-
- // Tree
- +SVerticalBox::Slot()
- .FillHeight(1.f)
- [
- TreeViewPtr.ToSharedRef()
- ]
- // Tree title
- + SVerticalBox::Slot()
- .AutoHeight()
- .Padding(0, 2, 0, 0)
- [
- SNew(SBorder)
- .BorderBackgroundColor(FLinearColor(1.f, 1.f, 1.f, 1.f))
- .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
- .Padding(1.f)
- .Visibility(this, &SExtPathView::GetTreeTitleVisibility)
- ]
- + SVerticalBox::Slot()
- .AutoHeight()
- .Padding(2)
- [
- SNew(SBorder)
- .BorderBackgroundColor(FLinearColor(0.f, 0.f, 0.f, 0.1f))
- .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
- .Padding(2)
- .Visibility(this, &SExtPathView::GetTreeTitleVisibility)
- [
- SNew(SHorizontalBox)
- + SHorizontalBox::Slot()
- .HAlign(HAlign_Center)
- .Padding(4, 0, 0, 0)
- [
- SNew(STextBlock)
- .Font(FAppStyle::GetFontStyle("ContentBrowser.SourceTitleFont"))
- .ColorAndOpacity(FLinearColor(1, 1, 1, 0.4f))
- .Text(this, &SExtPathView::GetTreeTitle)
- .Visibility(this, &SExtPathView::GetTreeTitleVisibility)
- ]
- ]
- ]
- + SVerticalBox::Slot()
- .AutoHeight()
- .Padding(0)
- [
- SNew(SBorder)
- .BorderBackgroundColor(FLinearColor(1.f, 1.f, 1.f, 1.f))
- .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
- .Padding(1.f)
- .Visibility(this, &SExtPathView::GetTreeTitleVisibility)
- ]
- #endif // Tree Section <<
- #if ECB_TODO
- // Version
- + SVerticalBox::Slot()
- .AutoHeight()
- .Padding(2, 2, 0, 1)
- .HAlign(HAlign_Left)
- [
- SNew (SHorizontalBox)
- +SHorizontalBox::Slot().AutoWidth()
- [
- SNew(STextBlock).Text(this, &SExtPathView::GetPluginVersionText)
- .ColorAndOpacity(FLinearColor::Gray)
- ]
- ]
- #endif
- ];
- #if ECB_LEGACY
- // Load the asset registry module to listen for updates
- FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
- AssetRegistryModule.Get().OnPathAdded().AddSP( this, &SExtPathView::OnAssetRegistryPathAdded );
- AssetRegistryModule.Get().OnPathRemoved().AddSP( this, &SExtPathView::OnAssetRegistryPathRemoved );
- AssetRegistryModule.Get().OnFilesLoaded().AddSP( this, &SExtPathView::OnAssetRegistrySearchCompleted );
- #endif
- FExtContentBrowserSingleton::GetAssetRegistry().OnRootPathAdded().AddSP(this, &SExtPathView::OnAssetRegistryRootPathAdded);
- FExtContentBrowserSingleton::GetAssetRegistry().OnRootPathRemoved().AddSP(this, &SExtPathView::OnAssetRegistryRootPathRemoved);
- FExtContentBrowserSingleton::GetAssetRegistry().OnRootPathUpdated().AddSP(this, &SExtPathView::OnAssetRegistryRootPathUpdated);
- FExtContentBrowserSingleton::GetAssetRegistry().OnFolderStartGathering().AddSP(this, &SExtPathView::OnAssetRegistryFolderStartGathering);
- FExtContentBrowserSingleton::GetAssetRegistry().OnFolderFinishGathering().AddSP(this, &SExtPathView::OnAssetRegistryFolderFinishGathering);
- // Add all paths currently gathered from the asset registry
- Populate();
- // Always expand the game root initially
- static const FString GameRootName = TEXT("Game");
- for ( auto RootIt = TreeRootItems.CreateConstIterator(); RootIt; ++RootIt )
- {
- if ( (*RootIt)->FolderName == GameRootName )
- {
- TreeViewPtr->SetItemExpansion(*RootIt, true);
- }
- }
- }
- void SExtPathView::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
- {
- #if ECB_FEA_ASYNC_FOLDER_DISCOVERY
- if (FExtContentBrowserSingleton::GetAssetRegistry().GetAndTrimFolderGatherResult())
- {
- Populate();
- }
- #endif
- }
- void SExtPathView::SetSelectedPaths(const TArray<FString>& Paths)
- {
- if ( !ensure(TreeViewPtr.IsValid()) )
- {
- return;
- }
- if ( !SearchBoxPtr->GetText().IsEmpty() )
- {
- // Clear the search box so the selected paths will be visible
- SearchBoxPtr->SetText( FText::GetEmpty() );
- }
- // Prevent the selection changed delegate since the invoking code requested it
- FScopedPreventTreeItemChangedDelegate DelegatePrevention( SharedThis(this) );
- // If the selection was changed before all pending initial paths were found, stop attempting to select them
- PendingInitialPaths.Empty();
- // Clear the selection to start, then add the selected paths as they are found
- TreeViewPtr->ClearSelection();
- TArray<FString> RootContentPaths;
- FExtContentBrowserSingleton::GetAssetRegistry().QueryRootContentPaths(RootContentPaths);
- for (int32 PathIdx = 0; PathIdx < Paths.Num(); ++PathIdx)
- {
- const FString& Path = Paths[PathIdx];
- TArray<FString> PathItemList;
- GetPathItemList(Path, RootContentPaths, PathItemList, /*bIncludeRootPath*/true);
- if ( PathItemList.Num() )
- {
- // There is at least one element in the path
- TArray<TSharedPtr<FTreeItem>> TreeItems;
- // Find the first item in the root items list
- for ( int32 RootItemIdx = 0; RootItemIdx < TreeRootItems.Num(); ++RootItemIdx )
- {
- if ( TreeRootItems[RootItemIdx]->FolderPath == PathItemList[0] )
- {
- // Found the first item in the path
- TreeItems.Add(TreeRootItems[RootItemIdx]);
- break;
- }
- }
- // If found in the root items list, try to find the childmost item matching the path
- if ( TreeItems.Num() > 0 )
- {
- for ( int32 PathItemIdx = 1; PathItemIdx < PathItemList.Num(); ++PathItemIdx )
- {
- const FString& PathItemName = PathItemList[PathItemIdx];
- const TSharedPtr<FTreeItem> ChildItem = TreeItems.Last()->GetChild(PathItemName);
- if ( ChildItem.IsValid() )
- {
- // Update tree items list
- TreeItems.Add(ChildItem);
- }
- else
- {
- // Could not find the child item
- break;
- }
- }
- // Expand all the tree folders up to but not including the last one.
- for (int32 ItemIdx = 0; ItemIdx < TreeItems.Num() - 1; ++ItemIdx)
- {
- TreeViewPtr->SetItemExpansion(TreeItems[ItemIdx], true);
- }
- // Set the selection to the closest found folder and scroll it into view
- TreeViewPtr->SetItemSelection(TreeItems.Last(), true);
- TreeViewPtr->RequestScrollIntoView(TreeItems.Last());
- }
- else
- {
- // Could not even find the root path... skip
- }
- }
- else
- {
- // No path items... skip
- }
- }
- }
- void SExtPathView::ClearSelection()
- {
- // Prevent the selection changed delegate since the invoking code requested it
- FScopedPreventTreeItemChangedDelegate DelegatePrevention( SharedThis(this) );
- // If the selection was changed before all pending initial paths were found, stop attempting to select them
- PendingInitialPaths.Empty();
- // Clear the selection to start, then add the selected paths as they are found
- TreeViewPtr->ClearSelection();
- }
- int32 SExtPathView::GetNumPathsSelected() const
- {
- return TreeViewPtr->GetNumItemsSelected();
- }
- FString SExtPathView::GetSelectedPath() const
- {
- TArray<TSharedPtr<FTreeItem>> Items = TreeViewPtr->GetSelectedItems();
- if (Items.Num() > 0)
- {
- FString& FolderPath = Items[0]->FolderPath;
- return FolderPath;
- }
- return FString();
- }
- TArray<FString> SExtPathView::GetSelectedPaths() const
- {
- TArray<FString> RetArray;
- TArray<TSharedPtr<FTreeItem>> Items = TreeViewPtr->GetSelectedItems();
- for (int32 ItemIdx = 0; ItemIdx < Items.Num(); ++ItemIdx)
- {
- FString& FolderPath = Items[ItemIdx]->FolderPath;
- RetArray.Add(FolderPath);
- }
- return RetArray;
- }
- TSharedPtr<FTreeItem> SExtPathView::AddPath(const FString& InPath, const FString* RootPathPtr/* = nullptr*/, bool bUserNamed)
- {
- if ( !ensure(TreeViewPtr.IsValid()) )
- {
- // No tree view for some reason
- return TSharedPtr<FTreeItem>();
- }
- FString Path = InPath;
- FString RootPath;
- if (RootPathPtr != nullptr)
- {
- RootPath = *RootPathPtr;
- }
- TArray<FString> RootContentPaths;
- FExtContentBrowserSingleton::GetAssetRegistry().QueryRootContentPaths(RootContentPaths);
- TArray<FString> PathItemList;
- GetPathItemList(Path, /*{ RootPath }*/RootContentPaths, PathItemList, /*bIncludeRootPath*/ true);
- if ( PathItemList.Num() )
- {
- FString ContentRootPath = PathItemList[0];
- // There is at least one element in the path
- TSharedPtr<FTreeItem> CurrentItem;
- // Find the first item in the root items list
- for ( int32 RootItemIdx = 0; RootItemIdx < TreeRootItems.Num(); ++RootItemIdx )
- {
- if ( TreeRootItems[RootItemIdx]->FolderPath == ContentRootPath)
- {
- // Found the first item in the path
- CurrentItem = TreeRootItems[RootItemIdx];
- break;
- }
- }
- // Roots may or may not exist, add the root here if it doesn't
- if ( !CurrentItem.IsValid() )
- {
- CurrentItem = AddRootItem(ContentRootPath);
- }
- // Found or added the root item?
- if ( CurrentItem.IsValid() )
- {
- // Now add children as necessary
- const bool bDisplayEmpty = GetDefault<UExtContentBrowserSettings>()->DisplayEmptyFolders;
- const bool bDisplayDev = GetDefault<UExtContentBrowserSettings>()->GetDisplayDevelopersFolder();
- const bool bDisplayL10N = GetDefault<UExtContentBrowserSettings>()->GetDisplayL10NFolder();
- for ( int32 PathItemIdx = 1; PathItemIdx < PathItemList.Num(); ++PathItemIdx )
- {
- const FString& PathItemName = PathItemList[PathItemIdx];
- TSharedPtr<FTreeItem> ChildItem = CurrentItem->GetChild(PathItemName);
- // If it does not exist, Create the child item
- if ( !ChildItem.IsValid() )
- {
- const FString FolderName = PathItemName;
- const FString FolderPath = CurrentItem->FolderPath + "/" + PathItemName;
- ChildItem = MakeShareable( new FTreeItem(FText::FromString(FolderName), FolderName, FolderPath, RootPath, CurrentItem, bUserNamed) );
- CurrentItem->Children.Add(ChildItem);
- CurrentItem->RequestSortChildren();
- TreeViewPtr->RequestTreeRefresh();
- // If we have pending initial paths, and this path added the path, we should select it now
- if ( PendingInitialPaths.Num() > 0 && PendingInitialPaths.Contains(FolderPath) )
- {
- RecursiveExpandParents(ChildItem);
- TreeViewPtr->SetItemSelection(ChildItem, true);
- TreeViewPtr->RequestScrollIntoView(ChildItem);
- }
- }
- else
- {
- //If the child item does exist, ensure its folder path is correct (may differ when renaming parent folder)
- ChildItem->FolderPath = CurrentItem->FolderPath + "/" + PathItemName;
- }
- CurrentItem = ChildItem;
- }
- if ( bUserNamed && CurrentItem->Parent.IsValid() )
- {
- // If we were creating a new item, select it, scroll it into view, expand the parent
- RecursiveExpandParents(CurrentItem);
- TreeViewPtr->RequestScrollIntoView(CurrentItem);
- TreeViewPtr->SetSelection(CurrentItem);
- }
- else
- {
- CurrentItem->bNamingFolder = false;
- }
- // Root: Is Loading
- if (!CurrentItem->Parent.IsValid())
- {
- const bool bIsLoading = ExtContentBrowserUtils::IsFolderBackgroundGathering(CurrentItem->FolderPath);
- CurrentItem->bLoading = bIsLoading;
- }
-
- // Update Root's loading status
- if (CurrentItem->Parent.IsValid())
- {
- TSharedPtr<FTreeItem> RootOfCurrentItem = FindItemRecursive(RootPath);
- if (RootOfCurrentItem.IsValid())
- {
- const bool bIsLoading = ExtContentBrowserUtils::IsFolderBackgroundGathering(RootPath);
- RootOfCurrentItem->bLoading = bIsLoading;
- if (bIsLoading)
- {
- FName GatheringFolder = ExtContentBrowserUtils::GetCurrentGatheringFolder();
- if (GatheringFolder != NAME_None)
- {
- FString LoadingFolder = ExtContentBrowserUtils::GetCurrentGatheringFolder().ToString();
- if (LoadingFolder.StartsWith(RootPath))
- {
- LoadingFolder.RemoveFromStart(RootPath);
- LoadingFolder.RemoveFromStart(TEXT("/"));
- RootOfCurrentItem->LoadingStatus = LoadingFolder;
- }
- }
- }
- }
- }
- }
- return CurrentItem;
- }
- return TSharedPtr<FTreeItem>();
- }
- bool SExtPathView::RemovePath(const FString& Path)
- {
- if ( !ensure(TreeViewPtr.IsValid()) )
- {
- // No tree view for some reason
- return false;
- }
- if ( Path.IsEmpty() )
- {
- // There were no elements in the path, cannot remove nothing
- return false;
- }
- // Find the folder in the tree
- TSharedPtr<FTreeItem> ItemToRemove = FindItemRecursive(Path);
- if ( ItemToRemove.IsValid() )
- {
- // Found the folder to remove. Remove it.
- if ( ItemToRemove->Parent.IsValid() )
- {
- // Remove the folder from its parent's list
- ItemToRemove->Parent.Pin()->Children.Remove(ItemToRemove);
- }
- else
- {
- // This is a root item. Remove the folder from the root items list.
- TreeRootItems.Remove(ItemToRemove);
- }
- // Refresh the tree
- TreeViewPtr->RequestTreeRefresh();
- return true;
- }
- else
- {
- // Did not find the folder to remove
- return false;
- }
- }
- void SExtPathView::RenameFolder(const FString& FolderToRename)
- {
- TArray<TSharedPtr<FTreeItem>> Items = TreeViewPtr->GetSelectedItems();
- for (int32 ItemIdx = 0; ItemIdx < Items.Num(); ++ItemIdx)
- {
- TSharedPtr<FTreeItem>& Item = Items[ItemIdx];
- if (Item.IsValid())
- {
- if (Item->FolderPath == FolderToRename)
- {
- Item->bNamingFolder = true;
- TreeViewPtr->SetSelection(Item);
- TreeViewPtr->RequestScrollIntoView(Item);
- break;
- }
- }
- }
- }
- void SExtPathView::SyncToAssets( const TArray<FExtAssetData>& AssetDataList, const bool bAllowImplicitSync )
- {
- SyncToInternal(AssetDataList, TArray<FString>(), bAllowImplicitSync);
- }
- void SExtPathView::SyncToFolders( const TArray<FString>& FolderList, const bool bAllowImplicitSync )
- {
- SyncToInternal(TArray<FExtAssetData>(), FolderList, bAllowImplicitSync);
- }
- void SExtPathView::SyncTo( const FExtContentBrowserSelection& ItemSelection, const bool bAllowImplicitSync )
- {
- SyncToInternal(ItemSelection.SelectedAssets, ItemSelection.SelectedFolders, bAllowImplicitSync);
- }
- void SExtPathView::SyncToInternal( const TArray<FExtAssetData>& AssetDataList, const TArray<FString>& FolderPaths, const bool bAllowImplicitSync )
- {
- TArray<TSharedPtr<FTreeItem>> SyncTreeItems;
- // Clear the filter
- SearchBoxPtr->SetText(FText::GetEmpty());
- TSet<FString> PackagePaths = TSet<FString>(FolderPaths);
- for (const FExtAssetData& AssetData : AssetDataList)
- {
- FString PackagePath = AssetData.GetFolderPath();
- PackagePaths.Add(PackagePath);
- }
- for (const FString& PackagePath : PackagePaths)
- {
- if ( !PackagePath.IsEmpty() )
- {
- TSharedPtr<FTreeItem> Item = FindItemRecursive(PackagePath);
- if ( Item.IsValid() )
- {
- SyncTreeItems.Add(Item);
- }
- }
- }
- if ( SyncTreeItems.Num() > 0 )
- {
- if (bAllowImplicitSync)
- {
- // Prune the current selection so that we don't unnecessarily change the path which might disorientate the user.
- // If a parent tree item is currently selected we don't need to clear it and select the child
- auto SelectedTreeItems = TreeViewPtr->GetSelectedItems();
- for (int32 Index = 0; Index < SelectedTreeItems.Num(); ++Index)
- {
- // For each item already selected in the tree
- auto AlreadySelectedTreeItem = SelectedTreeItems[Index];
- if (!AlreadySelectedTreeItem.IsValid())
- {
- continue;
- }
- // Check to see if any of the items to sync are already synced
- for (int32 ToSyncIndex = SyncTreeItems.Num()-1; ToSyncIndex >= 0; --ToSyncIndex)
- {
- auto ToSyncItem = SyncTreeItems[ToSyncIndex];
- if (ToSyncItem == AlreadySelectedTreeItem || ToSyncItem->IsChildOf(*AlreadySelectedTreeItem.Get()))
- {
- // A parent is already selected
- SyncTreeItems.Pop();
- }
- else if (ToSyncIndex == 0)
- {
- // AlreadySelectedTreeItem is not required for SyncTreeItems, so deselect it
- TreeViewPtr->SetItemSelection(AlreadySelectedTreeItem, false);
- }
- }
- }
- }
- else
- {
- // Explicit sync so just clear the selection
- TreeViewPtr->ClearSelection();
- }
- // SyncTreeItems should now only contain items which aren't already shown explicitly or implicitly (as a child)
- for ( auto ItemIt = SyncTreeItems.CreateConstIterator(); ItemIt; ++ItemIt )
- {
- RecursiveExpandParents(*ItemIt);
- TreeViewPtr->SetItemSelection(*ItemIt, true);
- }
- // > 0 as some may have been popped off in the code above
- if (SyncTreeItems.Num() > 0)
- {
- // Scroll the first item into view if applicable
- TreeViewPtr->RequestScrollIntoView(SyncTreeItems[0]);
- }
- }
- }
- TSharedPtr<FTreeItem> SExtPathView::FindItemRecursive(const FString& Path) const
- {
- for (auto TreeItemIt = TreeRootItems.CreateConstIterator(); TreeItemIt; ++TreeItemIt)
- {
- if ( (*TreeItemIt)->FolderPath == Path)
- {
- // This root item is the path
- return *TreeItemIt;
- }
- // Try to find the item under this root
- TSharedPtr<FTreeItem> Item = (*TreeItemIt)->FindItemRecursive(Path);
- if ( Item.IsValid() )
- {
- // The item was found under this root
- return Item;
- }
- }
- return TSharedPtr<FTreeItem>();
- }
- void SExtPathView::ApplyHistoryData ( const FHistoryData& History )
- {
- #if ECB_WIP_HISTORY
- // Prevent the selection changed delegate because it would add more history when we are just setting a state
- FScopedPreventTreeItemChangedDelegate DelegatePrevention( SharedThis(this) );
- // Update paths
- TArray<FString> SelectedPaths;
- for (const FName& HistoryPath : History.SourcesData.PackagePaths)
- {
- SelectedPaths.Add(HistoryPath.ToString());
- }
- SetSelectedPaths(SelectedPaths);
- #endif
- }
- void SExtPathView::SaveSettings(const FString& IniFilename, const FString& IniSection, const FString& SettingsString) const
- {
- FString SelectedPathsString;
- TArray< TSharedPtr<FTreeItem> > PathItems = TreeViewPtr->GetSelectedItems();
- for ( auto PathIt = PathItems.CreateConstIterator(); PathIt; ++PathIt )
- {
- if ( SelectedPathsString.Len() > 0 )
- {
- SelectedPathsString += TEXT(",");
- }
- SelectedPathsString += (*PathIt)->FolderPath;
- }
- GConfig->SetString(*IniSection, *(SettingsString + TEXT(".SelectedPaths")), *SelectedPathsString, IniFilename);
- }
- void SExtPathView::LoadSettings(const FString& IniFilename, const FString& IniSection, const FString& SettingsString)
- {
- // Selected Paths
- FString SelectedPathsString;
- TArray<FString> NewSelectedPaths;
- if ( GConfig->GetString(*IniSection, *(SettingsString + TEXT(".SelectedPaths")), SelectedPathsString, IniFilename) )
- {
- SelectedPathsString.ParseIntoArray(NewSelectedPaths, TEXT(","), /*bCullEmpty*/true);
- }
- if ( NewSelectedPaths.Num() > 0 )
- {
- FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
- const bool bDiscoveringAssets = AssetRegistryModule.Get().IsLoadingAssets();
- if ( bDiscoveringAssets )
- {
- // Keep track if we changed at least one source so we know to fire the bulk selection changed delegate later
- bool bSelectedAtLeastOnePath = false;
- {
- // Prevent the selection changed delegate since we are selecting one path at a time. A bulk event will be fired later if needed.
- FScopedPreventTreeItemChangedDelegate DelegatePrevention( SharedThis(this) );
- // Clear any previously selected paths
- TreeViewPtr->ClearSelection();
- // If the selected paths is empty, the path was "All assets"
- // This should handle that case properly
- for (int32 PathIdx = 0; PathIdx < NewSelectedPaths.Num(); ++PathIdx)
- {
- const FString& Path = NewSelectedPaths[PathIdx];
- if ( ExplicitlyAddPathToSelection(Path) )
- {
- bSelectedAtLeastOnePath = true;
- }
- else
- {
- // If we could not initially select these paths, but are still discovering assets, add them to a pending list to select them later
- PendingInitialPaths.Add(Path);
- }
- }
- }
- if ( bSelectedAtLeastOnePath )
- {
- // Send the first selected item with the notification
- const TArray<TSharedPtr<FTreeItem>> SelectedItems = TreeViewPtr->GetSelectedItems();
- check(SelectedItems.Num() > 0);
- // Signal a single selection changed event to let any listeners know that paths have changed
- TreeSelectionChanged( SelectedItems[0], ESelectInfo::Direct );
- }
- }
- else
- {
- // If all assets are already discovered, just select paths the best we can
- SetSelectedPaths(NewSelectedPaths);
- // Send the first selected item with the notification
- const TArray<TSharedPtr<FTreeItem>> SelectedItems = TreeViewPtr->GetSelectedItems();
- if (SelectedItems.Num() > 0)
- {
- // Signal a single selection changed event to let any listeners know that paths have changed
- TreeSelectionChanged( SelectedItems[0], ESelectInfo::Direct );
- }
- }
- }
- }
- EActiveTimerReturnType SExtPathView::SetFocusPostConstruct( double InCurrentTime, float InDeltaTime )
- {
- FWidgetPath WidgetToFocusPath;
- FSlateApplication::Get().GeneratePathToWidgetUnchecked( SearchBoxPtr.ToSharedRef(), WidgetToFocusPath );
- FSlateApplication::Get().SetKeyboardFocus( WidgetToFocusPath, EFocusCause::SetDirectly );
- return EActiveTimerReturnType::Stop;
- }
- EActiveTimerReturnType SExtPathView::TriggerRepopulate(double InCurrentTime, float InDeltaTime)
- {
- Populate();
- return EActiveTimerReturnType::Stop;
- }
- TSharedPtr<SWidget> SExtPathView::MakePathViewContextMenu()
- {
- if (!bAllowContextMenu || !OnGetFolderContextMenu.IsBound())
- {
- return nullptr;
- }
- const TArray<FString> SelectedPaths = GetSelectedPaths();
- if (SelectedPaths.Num() == 0)
- {
- return nullptr;
- }
- return OnGetFolderContextMenu.Execute(SelectedPaths, OnGetPathContextMenuExtender, NULL);
- }
- void SExtPathView::OnCreateNewFolder(const FString& FolderName, const FString& FolderPath)
- {
- AddPath(FolderPath / FolderName);
- }
- bool SExtPathView::ExplicitlyAddPathToSelection(const FString& Path)
- {
- if ( !ensure(TreeViewPtr.IsValid()) )
- {
- return false;
- }
- TArray<FString> PathItemList;
- Path.ParseIntoArray(PathItemList, TEXT("/"), /*InCullEmpty=*/true);
- if ( PathItemList.Num() )
- {
- // There is at least one element in the path
- TSharedPtr<FTreeItem> RootItem;
- // Find the first item in the root items list
- for ( int32 RootItemIdx = 0; RootItemIdx < TreeRootItems.Num(); ++RootItemIdx )
- {
- if ( TreeRootItems[RootItemIdx]->FolderPath == PathItemList[0] )
- {
- // Found the first item in the path
- RootItem = TreeRootItems[RootItemIdx];
- break;
- }
- }
- // If found in the root items list, try to find the item matching the path
- if ( RootItem.IsValid() )
- {
- TSharedPtr<FTreeItem> FoundItem = RootItem->FindItemRecursive(Path);
- if ( FoundItem.IsValid() )
- {
- // Set the selection to the closest found folder and scroll it into view
- RecursiveExpandParents(FoundItem);
- TreeViewPtr->SetItemSelection(FoundItem, true);
- TreeViewPtr->RequestScrollIntoView(FoundItem);
- return true;
- }
- }
- }
- return false;
- }
- bool SExtPathView::ShouldAllowTreeItemChangedDelegate() const
- {
- return PreventTreeItemChangedDelegateCount == 0;
- }
- void SExtPathView::RecursiveExpandParents(const TSharedPtr<FTreeItem>& Item)
- {
- if ( Item->Parent.IsValid() )
- {
- RecursiveExpandParents(Item->Parent.Pin());
- TreeViewPtr->SetItemExpansion(Item->Parent.Pin(), true);
- }
- }
- TSharedPtr<struct FTreeItem> SExtPathView::AddRootItem( const FString& InFolderName )
- {
- // Make sure the item is not already in the list
- for ( int32 RootItemIdx = 0; RootItemIdx < TreeRootItems.Num(); ++RootItemIdx )
- {
- if ( TreeRootItems[RootItemIdx]->FolderName == InFolderName )
- {
- // The root to add was already in the list return it here
- return TreeRootItems[RootItemIdx];
- }
- }
- TSharedPtr<struct FTreeItem> NewItem = nullptr;
- const bool bIsValidRootFolder = true;
- if (bIsValidRootFolder)
- {
- const FText DisplayName = ExtContentBrowserUtils::GetRootDirDisplayName(InFolderName);
- NewItem = MakeShareable( new FTreeItem(DisplayName, /*FolderName*/ InFolderName, /*InFolderPath*/ InFolderName, /*InRootFolderPath*/InFolderName, TSharedPtr<FTreeItem>()));
- TreeRootItems.Add( NewItem );
- TreeViewPtr->RequestTreeRefresh();
- }
- return NewItem;
- }
- TSharedRef<ITableRow> SExtPathView::GenerateTreeRow( TSharedPtr<FTreeItem> TreeItem, const TSharedRef<STableViewBase>& OwnerTable )
- {
- check(TreeItem.IsValid());
- return
- SNew( STableRow< TSharedPtr<FTreeItem> >, OwnerTable )
- .OnDragDetected( this, &SExtPathView::OnFolderDragDetected )
- [
- SNew(SExtAssetTreeItem)
- .TreeItem(TreeItem)
- .OnAssetsOrPathsDragDropped(this, &SExtPathView::TreeAssetsOrPathsDropped)
- .OnFilesDragDropped(this, &SExtPathView::TreeFilesDropped)
- .IsItemExpanded(this, &SExtPathView::IsTreeItemExpanded, TreeItem)
- .HighlightText(this, &SExtPathView::GetHighlightText)
- .IsSelected(this, &SExtPathView::IsTreeItemSelected, TreeItem)
- ];
- }
- void SExtPathView::TreeItemScrolledIntoView( TSharedPtr<FTreeItem> TreeItem, const TSharedPtr<ITableRow>& Widget )
- {
- if ( TreeItem->bNamingFolder && Widget.IsValid() && Widget->GetContent().IsValid() )
- {
- TreeItem->OnRenamedRequestEvent.Broadcast();
- }
- }
- void SExtPathView::GetChildrenForTree( TSharedPtr< FTreeItem > TreeItem, TArray< TSharedPtr<FTreeItem> >& OutChildren )
- {
- TreeItem->SortChildrenIfNeeded();
- OutChildren = TreeItem->Children;
- }
- void SExtPathView::SetTreeItemExpansionRecursive( TSharedPtr< FTreeItem > TreeItem, bool bInExpansionState )
- {
- TreeViewPtr->SetItemExpansion(TreeItem, bInExpansionState);
- // Recursively go through the children.
- for(auto It = TreeItem->Children.CreateIterator(); It; ++It)
- {
- SetTreeItemExpansionRecursive( *It, bInExpansionState );
- }
- }
- void SExtPathView::TreeSelectionChanged( TSharedPtr< FTreeItem > TreeItem, ESelectInfo::Type /*SelectInfo*/ )
- {
- if ( ShouldAllowTreeItemChangedDelegate() )
- {
- const TArray<TSharedPtr<FTreeItem>> SelectedItems = TreeViewPtr->GetSelectedItems();
- LastSelectedPaths.Empty();
- for (int32 ItemIdx = 0; ItemIdx < SelectedItems.Num(); ++ItemIdx)
- {
- const TSharedPtr<FTreeItem> Item = SelectedItems[ItemIdx];
- if ( !ensure(Item.IsValid()) )
- {
- // All items must exist
- continue;
- }
- // Keep track of the last paths that we broadcasted for selection reasons when filtering
- LastSelectedPaths.Add(Item->FolderPath);
- }
- if (OnPathSelected.IsBound() )
- {
- if ( TreeItem.IsValid() )
- {
- OnPathSelected.Execute(TreeItem->FolderPath);
- }
- else
- {
- OnPathSelected.Execute(TEXT(""));
- }
- }
- }
- if (TreeItem.IsValid())
- {
- // Prioritize the asset registry scan for the selected path
- FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
- AssetRegistryModule.Get().PrioritizeSearchPath(TreeItem->FolderPath / TEXT(""));
- }
- }
- void SExtPathView::TreeExpansionChanged( TSharedPtr< FTreeItem > TreeItem, bool bIsExpanded )
- {
- if ( ShouldAllowTreeItemChangedDelegate() )
- {
- TSet<TSharedPtr<FTreeItem>> ExpandedItemSet;
- TreeViewPtr->GetExpandedItems(ExpandedItemSet);
- const TArray<TSharedPtr<FTreeItem>> ExpandedItems = ExpandedItemSet.Array();
- LastExpandedPaths.Empty();
- for (int32 ItemIdx = 0; ItemIdx < ExpandedItems.Num(); ++ItemIdx)
- {
- const TSharedPtr<FTreeItem> Item = ExpandedItems[ItemIdx];
- if ( !ensure(Item.IsValid()) )
- {
- // All items must exist
- continue;
- }
- // Keep track of the last paths that we broadcasted for expansion reasons when filtering
- LastExpandedPaths.Add(Item->FolderPath);
- }
- }
- }
- void SExtPathView::OnAssetTreeSearchBoxChanged( const FText& InSearchText )
- {
- SearchBoxFolderFilter->SetRawFilterText( InSearchText );
- SearchBoxPtr->SetError( SearchBoxFolderFilter->GetFilterErrorText() );
- }
- void SExtPathView::OnAssetTreeSearchBoxCommitted(const FText& InSearchText, ETextCommit::Type InCommitType)
- {
- if (InCommitType == ETextCommit::OnCleared)
- {
- // Clear the search box and the filters
- SearchBoxPtr->SetText(FText::GetEmpty());
- OnAssetTreeSearchBoxChanged(FText::GetEmpty());
- FSlateApplication::Get().ClearKeyboardFocus(EFocusCause::Cleared);
- }
- }
- void SExtPathView::FilterUpdated()
- {
- Populate();
- }
- FText SExtPathView::GetHighlightText() const
- {
- return SearchBoxFolderFilter->GetRawFilterText();
- }
- void SExtPathView::Populate()
- {
- ECB_LOG(Display, TEXT("[SExtPathView] Populate"));
- // Don't allow the selection changed delegate to be fired here
- FScopedPreventTreeItemChangedDelegate DelegatePrevention( SharedThis(this) );
- // Clear all root items and clear selection
- TreeRootItems.Empty();
- TreeViewPtr->ClearSelection();
- const bool bFilteringByText = !SearchBoxFolderFilter->GetRawFilterText().IsEmpty();
- TArray<FString> RootContentPaths;
- FExtContentBrowserSingleton::GetAssetRegistry().QueryRootContentPaths(RootContentPaths);
- // Add default root folders to the asset tree if there's no filtering
- if ( !bFilteringByText )
- {
- for( TArray<FString>::TConstIterator RootPathIt( RootContentPaths ); RootPathIt; ++RootPathIt )
- {
- // Strip off any trailing forward slashes
- FString CleanRootPathName = *RootPathIt;
- while( CleanRootPathName.EndsWith( TEXT( "/" ) ) )
- {
- CleanRootPathName = CleanRootPathName.Mid( 0, CleanRootPathName.Len() - 1 );
- }
- AddPath(CleanRootPathName, &CleanRootPathName);
- }
- }
-
- for (const FString& RootContentPath : RootContentPaths)
- {
- if (FExtContentBrowserSingleton::GetAssetRegistry().IsFolderBackgroundGathering(*RootContentPath))
- {
- //continue;
- }
- // Get all paths
- TSet<FName> SubPaths;
- FExtContentBrowserSingleton::GetAssetRegistry().GetCachedSubPaths/*GetOrCacheSubPaths*//*safer*/(*RootContentPath, SubPaths, /*bRecursively = */ true);
- // Add all paths
- for (const FName& SubPath : SubPaths)
- {
- const FString Path = SubPath.ToString();
- // by sending the whole path we deliberately include any children of successful hits in the filtered list.
- if (SearchBoxFolderFilter->PassesFilter(Path))
- {
- TSharedPtr<FTreeItem> Item = AddPath(Path, &RootContentPath);
- if (Item.IsValid())
- {
- const bool bSelectedItem = LastSelectedPaths.Contains(Item->FolderPath);
- const bool bExpandedItem = LastExpandedPaths.Contains(Item->FolderPath);
- if (bFilteringByText || bSelectedItem)
- {
- RecursiveExpandParents(Item);
- }
- if (bSelectedItem)
- {
- // Tree items that match the last broadcasted paths should be re-selected them after they are added
- if (!TreeViewPtr->IsItemSelected(Item))
- {
- TreeViewPtr->SetItemSelection(Item, true);
- }
- TreeViewPtr->RequestScrollIntoView(Item);
- }
- if (bExpandedItem)
- {
- // Tree items that were previously expanded should be re-expanded when repopulating
- if (!TreeViewPtr->IsItemExpanded(Item))
- {
- TreeViewPtr->SetItemExpansion(Item, true);
- //RecursiveExpandParents(Item);
- }
- }
- }
- }
- }
- }
- SortRootItems();
- }
- void SExtPathView::SortRootItems()
- {
- // First sort the root items by their display name, but also making sure that content to appears before classes
- TreeRootItems.Sort([](const TSharedPtr<FTreeItem>& One, const TSharedPtr<FTreeItem>& Two) -> bool
- {
- static const FString ClassesPrefix = TEXT("Classes_");
- FString OneModuleName = One->FolderName;
- const bool bOneIsClass = OneModuleName.StartsWith(ClassesPrefix);
- if(bOneIsClass)
- {
- OneModuleName = OneModuleName.Mid(ClassesPrefix.Len());
- }
- FString TwoModuleName = Two->FolderName;
- const bool bTwoIsClass = TwoModuleName.StartsWith(ClassesPrefix);
- if(bTwoIsClass)
- {
- TwoModuleName = TwoModuleName.Mid(ClassesPrefix.Len());
- }
- // We want to sort content before classes if both items belong to the same module
- if(OneModuleName == TwoModuleName)
- {
- if(!bOneIsClass && bTwoIsClass)
- {
- return true;
- }
- return false;
- }
- return One->DisplayName.ToString() < Two->DisplayName.ToString();
- });
- // We have some manual sorting requirements that game must come before engine, and engine before everything else - we do that here after sorting everything by name
- // The array below is in the inverse order as we iterate through and move each match to the beginning of the root items array
- static const FString InverseSortOrder[] = {
- TEXT("Classes_Engine"),
- TEXT("Engine"),
- TEXT("Classes_Game"),
- TEXT("Game"),
- };
- for(const FString& SortItem : InverseSortOrder)
- {
- const int32 FoundItemIndex = TreeRootItems.IndexOfByPredicate([&SortItem](const TSharedPtr<FTreeItem>& TreeItem) -> bool
- {
- return TreeItem->FolderName == SortItem;
- });
- if(FoundItemIndex != INDEX_NONE)
- {
- TSharedPtr<FTreeItem> ItemToMove = TreeRootItems[FoundItemIndex];
- TreeRootItems.RemoveAt(FoundItemIndex);
- TreeRootItems.Insert(ItemToMove, 0);
- }
- }
- TreeViewPtr->RequestTreeRefresh();
- }
- void SExtPathView::PopulateFolderSearchStrings( const FString& FolderName, OUT TArray< FString >& OutSearchStrings ) const
- {
- OutSearchStrings.Add( FolderName );
- }
- FReply SExtPathView::OnFolderDragDetected(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
- {
- if ( MouseEvent.IsMouseButtonDown( EKeys::LeftMouseButton ) )
- {
- TArray<TSharedPtr<FTreeItem>> SelectedItems = TreeViewPtr->GetSelectedItems();
- if (SelectedItems.Num())
- {
- TArray<FString> PathNames;
- for ( auto ItemIt = SelectedItems.CreateConstIterator(); ItemIt; ++ItemIt )
- {
- PathNames.Add((*ItemIt)->FolderPath);
- }
- return FReply::Handled().BeginDragDrop(FAssetDragDropOp::New(PathNames));
- }
- }
- return FReply::Unhandled();
- }
- bool SExtPathView::FolderAlreadyExists(const TSharedPtr< FTreeItem >& TreeItem, TSharedPtr< FTreeItem >& ExistingItem)
- {
- ExistingItem.Reset();
- if ( TreeItem.IsValid() )
- {
- if ( TreeItem->Parent.IsValid() )
- {
- // This item has a parent, try to find it in its parent's children
- TSharedPtr<FTreeItem> ParentItem = TreeItem->Parent.Pin();
- for ( auto ChildIt = ParentItem->Children.CreateConstIterator(); ChildIt; ++ChildIt )
- {
- const TSharedPtr<FTreeItem>& Child = *ChildIt;
- if ( Child != TreeItem && Child->FolderName == TreeItem->FolderName )
- {
- // The item is in its parent already
- ExistingItem = Child;
- break;
- }
- }
- }
- else
- {
- // This item is part of the root set
- for ( auto RootIt = TreeRootItems.CreateConstIterator(); RootIt; ++RootIt )
- {
- const TSharedPtr<FTreeItem>& Root = *RootIt;
- if ( Root != TreeItem && Root->FolderName == TreeItem->FolderName )
- {
- // The item is part of the root set already
- ExistingItem = Root;
- break;
- }
- }
- }
- }
- return ExistingItem.IsValid();
- }
- void SExtPathView::RemoveFolderItem(const TSharedPtr< FTreeItem >& TreeItem)
- {
- if ( TreeItem.IsValid() )
- {
- if ( TreeItem->Parent.IsValid() )
- {
- // Remove this item from it's parent's list
- TreeItem->Parent.Pin()->Children.Remove(TreeItem);
- }
- else
- {
- // This was a root node, remove from the root list
- TreeRootItems.Remove(TreeItem);
- }
- TreeViewPtr->RequestTreeRefresh();
- }
- }
- void SExtPathView::TreeAssetsOrPathsDropped(const TArray<FAssetData>& AssetList, const TArray<FString>& AssetPaths, const TSharedPtr<FTreeItem>& TreeItem)
- {
- DragDropHandler::HandleDropOnAssetFolder(
- SharedThis(this),
- AssetList,
- AssetPaths,
- TreeItem->FolderPath,
- TreeItem->DisplayName,
- DragDropHandler::FExecuteCopyOrMove::CreateSP(this, &SExtPathView::ExecuteTreeDropCopy),
- DragDropHandler::FExecuteCopyOrMove::CreateSP(this, &SExtPathView::ExecuteTreeDropMove),
- DragDropHandler::FExecuteCopyOrMove::CreateSP(this, &SExtPathView::ExecuteTreeDropAdvancedCopy)
- );
- }
- void SExtPathView::TreeFilesDropped(const TArray<FString>& FileNames, const TSharedPtr<FTreeItem>& TreeItem)
- {
- FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
- AssetToolsModule.Get().ImportAssets( FileNames, TreeItem->FolderPath );
- }
- bool SExtPathView::IsTreeItemExpanded(TSharedPtr<FTreeItem> TreeItem) const
- {
- return TreeViewPtr->IsItemExpanded(TreeItem);
- }
- bool SExtPathView::IsTreeItemSelected(TSharedPtr<FTreeItem> TreeItem) const
- {
- return TreeViewPtr->IsItemSelected(TreeItem);
- }
- void SExtPathView::ExecuteTreeDropCopy(TArray<FAssetData> AssetList, TArray<FString> AssetPaths, FString DestinationPath)
- {
- if (AssetList.Num() > 0)
- {
- TArray<UObject*> DroppedObjects;
- ExtContentBrowserUtils::GetObjectsInAssetData(AssetList, DroppedObjects);
- ExtContentBrowserUtils::CopyAssets(DroppedObjects, DestinationPath);
- }
- if (AssetPaths.Num() > 0 && ExtContentBrowserUtils::CopyFolders(AssetPaths, DestinationPath))
- {
- TSharedPtr<FTreeItem> RootItem = FindItemRecursive(DestinationPath);
- if (RootItem.IsValid())
- {
- TreeViewPtr->SetItemExpansion(RootItem, true);
- // Select all the new folders
- TreeViewPtr->ClearSelection();
- for (const FString& AssetPath : AssetPaths)
- {
- const FString SubFolderName = FPackageName::GetLongPackageAssetName(AssetPath);
- const FString NewPath = DestinationPath + TEXT("/") + SubFolderName;
- TSharedPtr<FTreeItem> Item = FindItemRecursive(NewPath);
- if (Item.IsValid())
- {
- TreeViewPtr->SetItemSelection(Item, true);
- TreeViewPtr->RequestScrollIntoView(Item);
- }
- }
- }
- }
- }
- void SExtPathView::ExecuteTreeDropMove(TArray<FAssetData> AssetList, TArray<FString> AssetPaths, FString DestinationPath)
- {
- #if ECB_LEGACY
- if (AssetList.Num() > 0)
- {
- TArray<UObject*> DroppedObjects;
- ContentBrowserUtils::GetObjectsInAssetData(AssetList, DroppedObjects);
- ContentBrowserUtils::MoveAssets(DroppedObjects, DestinationPath);
- }
- // Prepare to fixup any asset paths that are favorites
- TArray<FMovedContentFolder> MovedFolders;
- for (const FString& OldPath : AssetPaths)
- {
- const FString SubFolderName = FPackageName::GetLongPackageAssetName(OldPath);
- const FString NewPath = DestinationPath + TEXT("/") + SubFolderName;
- MovedFolders.Add(FMovedContentFolder(OldPath, NewPath));
- }
- if (AssetPaths.Num() > 0 && ContentBrowserUtils::MoveFolders(AssetPaths, DestinationPath))
- {
- TSharedPtr<FTreeItem> RootItem = FindItemRecursive(DestinationPath);
- if (RootItem.IsValid())
- {
- TreeViewPtr->SetItemExpansion(RootItem, true);
- // Select all the new folders
- TreeViewPtr->ClearSelection();
- for (const FString& AssetPath : AssetPaths)
- {
- const FString SubFolderName = FPackageName::GetLongPackageAssetName(AssetPath);
- const FString NewPath = DestinationPath + TEXT("/") + SubFolderName;
- TSharedPtr<FTreeItem> Item = FindItemRecursive(NewPath);
- if (Item.IsValid())
- {
- TreeViewPtr->SetItemSelection(Item, true);
- TreeViewPtr->RequestScrollIntoView(Item);
- }
- }
- }
- OnFolderPathChanged.ExecuteIfBound(MovedFolders);
- }
- #endif
- }
- void SExtPathView::GetPathItemList(const FString& InPath, const TArray<FString>& InRootPaths, TArray<FString>& OutPathItemList, bool bIncludeRootPath) const
- {
- bool bFoundRootPath = false;
- FString RootPath;
- FString RelativePath;
- for (int32 Index = 0; Index < InRootPaths.Num(); ++Index)
- {
- RootPath = InRootPaths[Index];
- if (InPath.Find(*RootPath) == 0)
- {
- bFoundRootPath = true;
- RelativePath = InPath.Mid(FCString::Strlen(*RootPath));
- break;
- }
- }
- RelativePath.ParseIntoArray(OutPathItemList, TEXT("/"), /*InCullEmpty=*/true);
- if (bFoundRootPath && !RootPath.IsEmpty() && bIncludeRootPath)
- {
- OutPathItemList.Insert(RootPath, 0);
- }
- }
- void SExtPathView::ExecuteTreeDropAdvancedCopy(TArray<FAssetData> AssetList, TArray<FString> AssetPaths, FString DestinationPath)
- {
- ExtContentBrowserUtils::BeginAdvancedCopyPackages(AssetList, AssetPaths, DestinationPath);
- }
- void SExtPathView::OnAssetRegistryPathAdded(const FString& Path)
- {
- // by sending the whole path we deliberately include any children
- // of successful hits in the filtered list.
- if ( SearchBoxFolderFilter->PassesFilter( Path ) )
- {
- AddPath(Path);
- }
- }
- void SExtPathView::OnAssetRegistryPathRemoved(const FString& Path)
- {
- // by sending the whole path we deliberately include any children
- // of successful hits in the filtered list.
- if ( SearchBoxFolderFilter->PassesFilter( Path ) )
- {
- RemovePath(Path);
- }
- }
- void SExtPathView::OnAssetRegistryRootPathAdded(const FString& Path)
- {
- Populate();
- }
- void SExtPathView::OnAssetRegistryRootPathRemoved(const FString& Path)
- {
- Populate();
- ClearSelection();
- }
- void SExtPathView::OnAssetRegistryRootPathUpdated()
- {
- Populate();
- }
- void SExtPathView::OnAssetRegistryFolderStartGathering(const TArray<FString>& Paths)
- {
- //Populate();
- //ClearSelection();
- }
- void SExtPathView::OnAssetRegistryFolderFinishGathering(const FString& InPath, const FString& InRootPath)
- {
- //Populate();
- if (SearchBoxFolderFilter->PassesFilter(InPath))
- {
- AddPath(InPath, &InRootPath);
- TArray<FString> SelectedPath = GetSelectedPaths();
- if (SelectedPath.Contains(InPath) && OnPathSelected.IsBound())
- {
- // Refresh
- OnPathSelected.Execute(InPath);
- }
- }
- }
- void SExtPathView::OnAssetRegistrySearchCompleted()
- {
- // If there were any more initial paths, they no longer exist so clear them now.
- PendingInitialPaths.Empty();
- }
- void SExtPathView::OnFolderPopulated(const FString& Path)
- {
- OnAssetRegistryPathAdded(Path);
- }
- void SExtPathView::OnContentPathMountedOrDismounted( const FString& AssetPath, const FString& FilesystemPath )
- {
- /**
- * Hotfix
- * For some reason this widget sometime outlive the slate application shutdown
- * Validating that Slate application base is initialized will at least avoid the possible crash
- */
- if ( FSlateApplicationBase::IsInitialized() )
- {
- // A new content path has appeared, so we should refresh out root set of paths
- RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SExtPathView::TriggerRepopulate));
- }
- }
- void SExtPathView::OnClassHierarchyUpdated()
- {
- // The class hierarchy has changed in some way, so we need to refresh our set of paths
- RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SExtPathView::TriggerRepopulate));
- }
- void SExtPathView::HandleSettingChanged(FName PropertyName)
- {
- #if ECB_TODO
- if ((PropertyName == GET_MEMBER_NAME_CHECKED(UExtContentBrowserSettings, DisplayEmptyFolders)) ||
- (PropertyName == "DisplayDevelopersFolder") ||
- (PropertyName == "DisplayEngineFolder") ||
- (PropertyName == "DisplayPluginFolders") ||
- (PropertyName == "DisplayL10NFolder") ||
- (PropertyName == NAME_None)) // @todo: Needed if PostEditChange was called manually, for now
- {
- TSharedRef<FEmptyFolderVisibilityManager> EmptyFolderVisibilityManager = FContentBrowserSingleton::Get().GetEmptyFolderVisibilityManager();
- // If the dev or engine folder is no longer visible but we're inside it...
- const bool bDisplayEmpty = GetDefault<UExtContentBrowserSettings>()->DisplayEmptyFolders;
- const bool bDisplayDev = GetDefault<UExtContentBrowserSettings>()->GetDisplayDevelopersFolder();
- const bool bDisplayEngine = GetDefault<UExtContentBrowserSettings>()->GetDisplayEngineFolder();
- const bool bDisplayPlugins = GetDefault<UExtContentBrowserSettings>()->GetDisplayPluginFolders();
- const bool bDisplayL10N = GetDefault<UExtContentBrowserSettings>()->GetDisplayL10NFolder();
- if (!bDisplayEmpty || !bDisplayDev || !bDisplayEngine || !bDisplayPlugins || !bDisplayL10N)
- {
- const FString OldSelectedPath = GetSelectedPath();
- const ContentBrowserUtils::ECBFolderCategory OldFolderCategory = ContentBrowserUtils::GetFolderCategory(OldSelectedPath);
- if ((!bDisplayEmpty && !EmptyFolderVisibilityManager->ShouldShowPath(OldSelectedPath)) ||
- (!bDisplayDev && OldFolderCategory == ContentBrowserUtils::ECBFolderCategory::DeveloperContent) ||
- (!bDisplayEngine && (OldFolderCategory == ContentBrowserUtils::ECBFolderCategory::EngineContent || OldFolderCategory == ContentBrowserUtils::ECBFolderCategory::EngineClasses)) ||
- (!bDisplayPlugins && (OldFolderCategory == ContentBrowserUtils::ECBFolderCategory::PluginContent || OldFolderCategory == ContentBrowserUtils::ECBFolderCategory::PluginClasses)) ||
- (!bDisplayL10N && ContentBrowserUtils::IsLocalizationFolder(OldSelectedPath)))
- {
- // Set the folder back to the root, and refresh the contents
- TSharedPtr<FTreeItem> GameRoot = FindItemRecursive(TEXT("/Game"));
- if ( GameRoot.IsValid() )
- {
- TreeViewPtr->SetSelection(GameRoot);
- }
- else
- {
- TreeViewPtr->ClearSelection();
- }
- }
- }
- // Update our path view so that it can include/exclude the dev folder
- Populate();
- // If the dev or engine folder has become visible and we're inside it...
- if (bDisplayDev || bDisplayEngine || bDisplayPlugins || bDisplayL10N)
- {
- const FString NewSelectedPath = GetSelectedPath();
- const ContentBrowserUtils::ECBFolderCategory NewFolderCategory = ContentBrowserUtils::GetFolderCategory(NewSelectedPath);
- if ((bDisplayDev && NewFolderCategory == ContentBrowserUtils::ECBFolderCategory::DeveloperContent) ||
- (bDisplayEngine && (NewFolderCategory == ContentBrowserUtils::ECBFolderCategory::EngineContent || NewFolderCategory == ContentBrowserUtils::ECBFolderCategory::EngineClasses)) ||
- (bDisplayPlugins && (NewFolderCategory == ContentBrowserUtils::ECBFolderCategory::PluginContent || NewFolderCategory == ContentBrowserUtils::ECBFolderCategory::PluginClasses)) ||
- (bDisplayL10N && ContentBrowserUtils::IsLocalizationFolder(NewSelectedPath)))
- {
- // Refresh the contents
- OnPathSelected.ExecuteIfBound(NewSelectedPath);
- }
- }
- }
- #endif
- }
- void SFavoritePathView::Construct(const FArguments& InArgs)
- {
- SAssignNew(TreeViewPtr, STreeView< TSharedPtr<FTreeItem> >)
- .TreeItemsSource(&TreeRootItems)
- .OnGetChildren(this, &SFavoritePathView::GetChildrenForTree)
- .OnGenerateRow(this, &SFavoritePathView::GenerateTreeRow)
- .OnItemScrolledIntoView(this, &SFavoritePathView::TreeItemScrolledIntoView)
- .ItemHeight(18)
- .SelectionMode(InArgs._SelectionMode)
- .OnSelectionChanged(this, &SFavoritePathView::TreeSelectionChanged)
- .OnContextMenuOpening(this, &SFavoritePathView::MakePathViewContextMenu)
- .ClearSelectionOnClick(false);
- SExtPathView::Construct(InArgs);
- }
- void SFavoritePathView::Populate()
- {
- // Don't allow the selection changed delegate to be fired here
- FScopedPreventTreeItemChangedDelegate DelegatePrevention(SharedThis(this));
- // Clear all root items and clear selection
- TreeRootItems.Empty();
- TreeViewPtr->ClearSelection();
- const TArray<FString> FavoritePaths = ExtContentBrowserUtils::GetFavoriteFolders();
- // we have a text filter, expand all parents of matching folders
- for (const FString& Path : FavoritePaths)
- {
- // by sending the whole path we deliberately include any children
- // of successful hits in the filtered list.
- if (SearchBoxFolderFilter->PassesFilter(Path))
- {
- TSharedPtr<FTreeItem> Item = AddPath(Path);
- if (Item.IsValid())
- {
- const bool bSelectedItem = LastSelectedPaths.Contains(Item->FolderPath);
- if (bSelectedItem)
- {
- // Tree items that match the last broadcasted paths should be re-selected them after they are added
- TreeViewPtr->SetItemSelection(Item, true);
- TreeViewPtr->RequestScrollIntoView(Item);
- }
- }
- }
- }
- SortRootItems();
- }
- void SFavoritePathView::SaveSettings(const FString& IniFilename, const FString& IniSection, const FString& SettingsString) const
- {
- SExtPathView::SaveSettings(IniFilename, IniSection, SettingsString);
- FString FavoritePathsString;
- const TArray<FString> FavoritePaths = ExtContentBrowserUtils::GetFavoriteFolders();
- for (const FString& PathIt : FavoritePaths)
- {
- if (FavoritePathsString.Len() > 0)
- {
- FavoritePathsString += TEXT(",");
- }
- FavoritePathsString += PathIt;
- }
- GConfig->SetString(*IniSection, TEXT("FavoritePaths"), *FavoritePathsString, IniFilename);
- }
- void SFavoritePathView::LoadSettings(const FString& IniFilename, const FString& IniSection, const FString& SettingsString)
- {
- SExtPathView::LoadSettings(IniFilename, IniSection, SettingsString);
- // Selected Paths
- FString SelectedPathsString;
- TArray<FString> NewFavoritePaths;
- if (GConfig->GetString(*IniSection, TEXT("FavoritePaths"), SelectedPathsString, IniFilename))
- {
- SelectedPathsString.ParseIntoArray(NewFavoritePaths, TEXT(","), /*bCullEmpty*/true);
- }
- if (NewFavoritePaths.Num() > 0)
- {
- // Keep track if we changed at least one source so we know to fire the bulk selection changed delegate later
- bool bAddedAtLeastOnePath = false;
- {
- // If the selected paths is empty, the path was "All assets"
- // This should handle that case properly
- for (int32 PathIdx = 0; PathIdx < NewFavoritePaths.Num(); ++PathIdx)
- {
- const FString& Path = NewFavoritePaths[PathIdx];
- ExtContentBrowserUtils::AddFavoriteFolder(Path, false);
- bAddedAtLeastOnePath = true;
- }
- }
- if (bAddedAtLeastOnePath)
- {
- Populate();
- }
- }
- }
- void SFavoritePathView::OnAssetTreeSearchBoxChanged(const FText& InSearchText)
- {
- SExtPathView::OnAssetTreeSearchBoxChanged(InSearchText);
- OnFavoriteSearchChanged.ExecuteIfBound(InSearchText);
- }
- void SFavoritePathView::OnAssetTreeSearchBoxCommitted(const FText& InSearchText, ETextCommit::Type InCommitType)
- {
- SExtPathView::OnAssetTreeSearchBoxCommitted(InSearchText, InCommitType);
- OnFavoriteSearchCommitted.ExecuteIfBound(InSearchText, InCommitType);
- }
- void SFavoritePathView::SetSelectedPaths(const TArray<FString>& Paths)
- {
- if (!ensure(TreeViewPtr.IsValid()))
- {
- return;
- }
- if (!SearchBoxPtr->GetText().IsEmpty())
- {
- // Clear the search box so the selected paths will be visible
- SearchBoxPtr->SetText(FText::GetEmpty());
- }
- // Prevent the selection changed delegate since the invoking code requested it
- FScopedPreventTreeItemChangedDelegate DelegatePrevention(SharedThis(this));
- // If the selection was changed before all pending initial paths were found, stop attempting to select them
- PendingInitialPaths.Empty();
- // Clear the selection to start, then add the selected paths as they are found
- TreeViewPtr->ClearSelection();
- for (int32 PathIdx = 0; PathIdx < Paths.Num(); ++PathIdx)
- {
- const FString& Path = Paths[PathIdx];
- TArray<FString> PathItemList;
- Path.ParseIntoArray(PathItemList, TEXT("/"), /*InCullEmpty=*/true);
- if (PathItemList.Num())
- {
- // There is at least one element in the path
- TArray<TSharedPtr<FTreeItem>> TreeItems;
- // Find the first item in the root items list
- for (int32 RootItemIdx = 0; RootItemIdx < TreeRootItems.Num(); ++RootItemIdx)
- {
- if (TreeRootItems[RootItemIdx]->FolderName == PathItemList[0])
- {
- // Found the first item in the path
- TreeItems.Add(TreeRootItems[RootItemIdx]);
- break;
- }
- }
- // If found in the root items list, try to find the childmost item matching the path
- if (TreeItems.Num() > 0)
- {
- for (int32 PathItemIdx = 1; PathItemIdx < PathItemList.Num(); ++PathItemIdx)
- {
- const FString& PathItemName = PathItemList[PathItemIdx];
- const TSharedPtr<FTreeItem> ChildItem = TreeItems.Last()->GetChild(PathItemName);
- if (ChildItem.IsValid())
- {
- // Update tree items list
- TreeItems.Add(ChildItem);
- }
- else
- {
- // Could not find the child item
- break;
- }
- }
- // Set the selection to the closest found folder and scroll it into view
- TreeViewPtr->SetItemSelection(TreeItems.Last(), true);
- TreeViewPtr->RequestScrollIntoView(TreeItems.Last());
- }
- else
- {
- // Could not even find the root path... skip
- }
- }
- else
- {
- // No path items... skip
- }
- }
- }
- TSharedPtr<FTreeItem> SFavoritePathView::AddPath(const FString& InPath, const FString* RootPathPtr/* = nullptr*/, bool bUserNamed /*= false*/)
- {
- if (!ensure(TreeViewPtr.IsValid()))
- {
- // No tree view for some reason
- return TSharedPtr<FTreeItem>();
- }
- FString Path = InPath;
- FString RootPath;
- if (RootPathPtr != nullptr)
- {
- RootPath = *RootPathPtr;
- if (Path.Find(*RootPath) == 0)
- {
- Path = Path.Mid(FCString::Strlen(*RootPath));
- }
- }
- TArray<FString> PathItemList;
- Path.ParseIntoArray(PathItemList, TEXT("/"), /*InCullEmpty=*/true);
- if (PathItemList.Num())
- {
- // There is at least one element in the path
- const FString FolderName = PathItemList.Last();
- const FString FolderPath = Path;
- // Make sure the item is not already in the list
- for (int32 RootItemIdx = 0; RootItemIdx < TreeRootItems.Num(); ++RootItemIdx)
- {
- if (TreeRootItems[RootItemIdx]->FolderPath == FolderPath)
- {
- // The root to add was already in the list return it here
- return TreeRootItems[RootItemIdx];
- }
- }
- TSharedPtr<struct FTreeItem> NewItem = nullptr;
- // If this isn't an engine folder or we want to show them, add
- const bool bDisplayEngine = GetDefault<UExtContentBrowserSettings>()->GetDisplayEngineFolder();
- const bool bDisplayPlugins = GetDefault<UExtContentBrowserSettings>()->GetDisplayPluginFolders();
- const bool bDisplayCpp = GetDefault<UExtContentBrowserSettings>()->GetDisplayCppFolders();
- const bool bDisplayLoc = GetDefault<UExtContentBrowserSettings>()->GetDisplayL10NFolder();
- // Filter out classes folders if we're not showing them.
- if (!bDisplayCpp && ExtContentBrowserUtils::IsClassesFolder(FolderName))
- {
- return nullptr;
- }
- // Filter out plugin folders
- bool bIsPlugin = false;
- EPluginLoadedFrom PluginSource = EPluginLoadedFrom::Engine; // init to avoid warning
- if (!bDisplayEngine || !bDisplayPlugins)
- {
- bIsPlugin = ExtContentBrowserUtils::IsPluginFolder(FolderName, &PluginSource);
- }
- if ((bDisplayEngine || !ExtContentBrowserUtils::IsEngineFolder(FolderName)) &&
- ((bDisplayEngine && bDisplayPlugins) || !(bIsPlugin && PluginSource == EPluginLoadedFrom::Engine)) &&
- (bDisplayPlugins || !(bIsPlugin && PluginSource == EPluginLoadedFrom::Project)) &&
- (bDisplayLoc || !ExtContentBrowserUtils::IsLocalizationFolder(FolderName)))
- {
- const FText DisplayName = ExtContentBrowserUtils::GetRootDirDisplayName(FolderName);
- NewItem = MakeShareable(new FTreeItem(FText::FromString(FolderName), FolderName, FolderPath, RootPath, TSharedPtr<FTreeItem>()));
- TreeRootItems.Add(NewItem);
- TreeViewPtr->RequestTreeRefresh();
- TreeViewPtr->SetSelection(NewItem);
- }
- return NewItem;
- }
- return TSharedPtr<FTreeItem>();
- }
- TSharedRef<ITableRow> SFavoritePathView::GenerateTreeRow(TSharedPtr<FTreeItem> TreeItem, const TSharedRef<STableViewBase>& OwnerTable)
- {
- check(TreeItem.IsValid());
- return
- SNew( STableRow< TSharedPtr<FTreeItem> >, OwnerTable )
- .OnDragDetected( this, &SFavoritePathView::OnFolderDragDetected )
- [
- SNew(SExtAssetTreeItem)
- .TreeItem(TreeItem)
- .OnAssetsOrPathsDragDropped(this, &SFavoritePathView::TreeAssetsOrPathsDropped)
- .OnFilesDragDropped(this, &SFavoritePathView::TreeFilesDropped)
- .IsItemExpanded(false)
- .HighlightText(this, &SFavoritePathView::GetHighlightText)
- .IsSelected(this, &SFavoritePathView::IsTreeItemSelected, TreeItem)
- .FontOverride(FAppStyle::GetFontStyle("ContentBrowser.SourceTreeItemFont"))
- ];
- }
- void SFavoritePathView::OnAssetRegistryPathAdded(const FString& Path)
- {
- }
- void SFavoritePathView::OnAssetRegistryPathRemoved(const FString& Path)
- {
- ExtContentBrowserUtils::RemoveFavoriteFolder(Path);
- Populate();
- }
- void SFavoritePathView::ExecuteTreeDropMove(TArray<FAssetData> AssetList, TArray<FString> AssetPaths, FString DestinationPath)
- {
- #if ECB_LEGACY
- if (AssetList.Num() > 0)
- {
- TArray<UObject*> DroppedObjects;
- ContentBrowserUtils::GetObjectsInAssetData(AssetList, DroppedObjects);
- ContentBrowserUtils::MoveAssets(DroppedObjects, DestinationPath);
- }
- // Prepare to fixup any asset paths that are favorites
- TArray<FMovedContentFolder> MovedFolders;
- for (const FString& OldPath : AssetPaths)
- {
- const FString SubFolderName = FPackageName::GetLongPackageAssetName(OldPath);
- const FString NewPath = DestinationPath + TEXT("/") + SubFolderName;
- MovedFolders.Add(FMovedContentFolder(OldPath, NewPath));
- }
- FixupFavoritesFromExternalChange(MovedFolders);
- ContentBrowserUtils::MoveFolders(AssetPaths, DestinationPath);
- #endif
- }
- FText SExtPathView::GetTreeTitle() const
- {
- const UExtContentBrowserSettings* ExtContentBrowserSettings = GetDefault<UExtContentBrowserSettings>();
- const bool bCacheMode = ExtContentBrowserSettings->bCacheMode;
- if (bCacheMode)
- {
- const FString& CacheFilePath = ExtContentBrowserSettings->CacheFilePath.FilePath;
- const FString FileName = FPaths::GetBaseFilename(CacheFilePath);
- return FText::Format(LOCTEXT("CacheModeTreeTitle", "{0}"), FText::FromString(FileName));
- }
- else
- {
- return TreeTitle;
- }
- }
- EVisibility SExtPathView::GetTreeTitleVisibility() const
- {
-
- const UExtContentBrowserSettings* ExtContentBrowserSettings = GetDefault<UExtContentBrowserSettings>();
- const bool bCacheMode = ExtContentBrowserSettings->bCacheMode;
- if (bCacheMode)
- {
- return EVisibility::Visible;
- }
- else
- {
- return EVisibility::Collapsed;
- }
- }
- #undef LOCTEXT_NAMESPACE
|