| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208 | // Copyright 2017-2021 marynate. All Rights Reserved.#include "SExtAssetView.h"#include "ExtContentBrowserSingleton.h"#include "ExtAssetThumbnail.h"#include "ExtAssetViewWidgets.h"#include "ExtAssetViewTypes.h"#include "ExtContentBrowserUtils.h"#include "ExtContentBrowserStyle.h"#include "SExtAssetDiscoveryIndicator.h"#include "ExtFrontendFilterBase.h"#include "ExtAssetDragDropOp.h"#include "Adapters/DragDropHandler.h"#include "HAL/FileManager.h"#include "UObject/UnrealType.h"#include "Widgets/SOverlay.h"#include "Engine/GameViewportClient.h"#include "Factories/Factory.h"#include "Framework/Commands/UIAction.h"#include "Textures/SlateIcon.h"#include "Misc/CommandLine.h"#include "Misc/ConfigCacheIni.h"#include "SlateOptMacros.h"#include "Framework/Application/SlateApplication.h"#include "Widgets/Images/SImage.h"#include "Widgets/Notifications/SProgressBar.h"#include "Widgets/Text/STextBlock.h"#include "Framework/MultiBox/MultiBoxBuilder.h"#include "Widgets/Input/SButton.h"#include "Widgets/Layout/SScrollBorder.h"#include "Widgets/Input/SComboButton.h"#include "Widgets/Input/SSlider.h"#include "Widgets/Notifications/SNotificationList.h"#include "Widgets/Layout/SSplitter.h"#include "Widgets/Colors/SColorBlock.h"#include "Widgets/Colors/SColorPicker.h"#include "Widgets/Layout/SGridPanel.h"#include "Framework/Docking/TabManager.h"#include "EditorStyleSet.h"#include "Engine/Blueprint.h"#include "Editor.h"#include "AssetSelection.h"#include "AssetRegistry/AssetRegistryModule.h"#include "IAssetTools.h"#include "AssetToolsModule.h"#include "DragAndDrop/AssetDragDropOp.h"#include "ObjectTools.h"#include "Framework/Notifications/NotificationManager.h"#include "HAL/PlatformApplicationMisc.h"#include "Misc/FileHelper.h"//#include "Misc/TextFilterUtils.h"#include "AssetRegistry/AssetRegistryState.h"#include "Materials/Material.h"//#include "DragDropHandler.h"#include "DesktopPlatformModule.h"#include "EditorWidgetsModule.h"#include "ContentBrowserDelegates.h"#include "ContentBrowserModule.h"#include "LevelEditor.h"#define LOCTEXT_NAMESPACE "ExtContentBrowser"#define MAX_THUMBNAIL_SIZE 4096namespace{	/** Time delay between recently added items being added to the filtered asset items list */	const double TimeBetweenAddingNewAssets = 0.3;//4.0;	/** Time delay between performing the last jump, and the jump term being reset */	const double JumpDelaySeconds = 2.0;}#if ECB_WIP_INITIAL_ASSETnamespace AssetViewUtils{	/** Higher performance version of FAssetData::IsUAsset()	 * Returns true if this is the primary asset in a package, true for maps and assets but false for secondary objects like class redirectors	 */	bool IsUAsset(const FAssetData& Item)	{		TextFilterUtils::FNameBufferWithNumber AssetNameBuffer(Item.AssetName);		TextFilterUtils::FNameBufferWithNumber PackageNameBuffer(Item.PackageName);		if (PackageNameBuffer.IsWide())		{			// Skip to final slash			const WIDECHAR* LongPackageAssetNameWide = PackageNameBuffer.GetWideNamePtr();			for (const WIDECHAR* CharPtr = PackageNameBuffer.GetWideNamePtr(); *CharPtr; ++CharPtr)			{				if (*CharPtr == '/')				{					LongPackageAssetNameWide = CharPtr + 1;				}			}			if (AssetNameBuffer.IsWide())			{				return !FCStringWide::Stricmp(LongPackageAssetNameWide, AssetNameBuffer.GetWideNamePtr());			}			else if (FCString::IsPureAnsi(LongPackageAssetNameWide))			{				// Convert PackageName to ANSI for comparison				ANSICHAR LongPackageAssetNameAnsi[NAME_WITH_NUMBER_SIZE];				FPlatformString::Convert(LongPackageAssetNameAnsi, NAME_WITH_NUMBER_SIZE, LongPackageAssetNameWide, FCStringWide::Strlen(LongPackageAssetNameWide) + 1);				return !FCStringAnsi::Stricmp(LongPackageAssetNameAnsi, AssetNameBuffer.GetAnsiNamePtr());			}		}		else if (!AssetNameBuffer.IsWide())		{			// Skip to final slash			const ANSICHAR* LongPackageAssetNameAnsi = PackageNameBuffer.GetAnsiNamePtr();			for (const ANSICHAR* CharPtr = PackageNameBuffer.GetAnsiNamePtr(); *CharPtr; ++CharPtr)			{				if (*CharPtr == '/')				{					LongPackageAssetNameAnsi = CharPtr + 1;				}			}			return !FCStringAnsi::Stricmp(LongPackageAssetNameAnsi, AssetNameBuffer.GetAnsiNamePtr());		}		return false;	}	class FInitialAssetFilter	{	public:		FInitialAssetFilter(bool InDisplayL10N, bool InDisplayEngine, bool InDisplayPlugins) :			bDisplayL10N(InDisplayL10N),			bDisplayEngine(InDisplayEngine),			bDisplayPlugins(InDisplayPlugins)		{			Init(InDisplayL10N, InDisplayEngine, InDisplayPlugins);		}		void Init(bool InDisplayL10N, bool InDisplayEngine, bool InDisplayPlugins)		{			ObjectRedirectorClassName = UObjectRedirector::StaticClass()->GetFName();			bDisplayL10N = InDisplayL10N;			bDisplayEngine = InDisplayEngine;			bDisplayPlugins = InDisplayPlugins;			Plugins = IPluginManager::Get().GetEnabledPluginsWithContent();			PluginNamesUpperWide.Reset(Plugins.Num());			PluginNamesUpperAnsi.Reset(Plugins.Num());			PluginLoadedFromEngine.Reset(Plugins.Num());			for (const TSharedRef<IPlugin>& PluginRef : Plugins)			{				FString& PluginNameUpperWide = PluginNamesUpperWide.Add_GetRef(PluginRef->GetName().ToUpper());				TextFilterUtils::TryConvertWideToAnsi(PluginNameUpperWide, PluginNamesUpperAnsi.AddDefaulted_GetRef());				PluginLoadedFromEngine.Add(PluginRef->GetLoadedFrom() == EPluginLoadedFrom::Engine);			}		}		FORCEINLINE bool PassesFilter(const FAssetData& Item) const		{			if (!PassesRedirectorMainAssetFilter(Item))			{				return false;			}			return PassesPackagePathFilter(Item.PackagePath);		}		FORCEINLINE bool PassesRedirectorMainAssetFilter(const FAssetData& Item) const		{			// Do not show redirectors if they are not the main asset in the uasset file.			if (Item.AssetClass == ObjectRedirectorClassName && !AssetViewUtils::IsUAsset(Item))			{				return false;			}			return true;		}		FORCEINLINE bool PassesPackagePathFilter(const FName& PackagePath) const		{			TextFilterUtils::FNameBufferWithNumber ObjectPathBuffer(PackagePath);			if (ObjectPathBuffer.IsWide())			{				return PassesPackagePathFilter(ObjectPathBuffer.GetWideNamePtr());			}			else			{				return PassesPackagePathFilter(ObjectPathBuffer.GetAnsiNamePtr());			}		}		template <typename CharType>		FORCEINLINE bool PassesPackagePathFilter(CharType* PackagePath) const		{			CharType* PathCh = PackagePath;			if (*PathCh++ != '/')			{				return true;			}			for (; *PathCh && *PathCh != '/'; ++PathCh)			{				*PathCh = TChar<CharType>::ToUpper(*PathCh);			}			if (*PathCh)			{				if (!bDisplayL10N)				{					if ((PathCh[1] == 'L' || PathCh[1] == 'l') &&						PathCh[2] == '1' &&						PathCh[3] == '0' &&						(PathCh[4] == 'N' || PathCh[4] == 'n') &&						(PathCh[5] == '/' || PathCh[5] == 0))					{						return false;					}				}				*PathCh = 0;			}			CharType* PackageRootFolderName = PackagePath + 1;			int32 PackageRootFolderNameLength = PathCh - PackageRootFolderName;			if (PackageRootFolderNameLength == 4 && TCString<CharType>::Strncmp(PackageRootFolderName, LITERAL(CharType, "GAME"), 4) == 0)			{				return true;			}			else if (PackageRootFolderNameLength == 6 && TCString<CharType>::Strncmp(PackageRootFolderName, LITERAL(CharType, "ENGINE"), 4) == 0)			{				return bDisplayEngine;			}			else			{				if (!bDisplayPlugins || !bDisplayEngine)				{					int32 PluginIndex = FindPluginNameUpper(PackageRootFolderName, PackageRootFolderNameLength);					if (PluginIndex != INDEX_NONE)					{						if (!bDisplayPlugins)						{							return false;						}						else if (!bDisplayEngine && PluginLoadedFromEngine[PluginIndex])						{							return false;						}					}				}			}			return true;		}	private:		FName ObjectRedirectorClassName;		bool bDisplayL10N;		bool bDisplayEngine;		bool bDisplayPlugins;		TArray<TArray<ANSICHAR>> PluginNamesUpperAnsi;		TArray<FString> PluginNamesUpperWide;		TArray<bool> PluginLoadedFromEngine;		TArray<TSharedRef<IPlugin>> Plugins;		FORCEINLINE int32 FindPluginNameUpper(const WIDECHAR* PluginNameUpper, int32 Length) const		{			int32 i = 0;			for (const FString& OtherPluginNameUpper : PluginNamesUpperWide)			{				if (OtherPluginNameUpper.Len() == Length && TCString<WIDECHAR>::Strcmp(PluginNameUpper, *OtherPluginNameUpper) == 0)				{					return i;				}				++i;			}			return INDEX_NONE;		}		FORCEINLINE int32 FindPluginNameUpper(const ANSICHAR* PluginNameUpper, int32 Length) const		{			const int32 LengthWithNull = Length + 1;			int32 i = 0;			for (const TArray<ANSICHAR>& OtherPluginNameUpper : PluginNamesUpperAnsi)			{				if (OtherPluginNameUpper.Num() == LengthWithNull && TCString<ANSICHAR>::Strcmp(PluginNameUpper, OtherPluginNameUpper.GetData()) == 0)				{					return i;				}				++i;			}			return INDEX_NONE;		}	};} // namespace AssetViewUtils#endif/////////////////////////////////////////////////////////////// WidgetHelpers implementation//namespace WidgetHelpers{	TSharedRef<SWidget> CreateColorWidget(FLinearColor* Color)	{		auto GetValue = [=] {			return *Color;		};		auto SetValue = [=](FLinearColor NewColor) {			*Color = NewColor;		};		auto OnGetMenuContent = [=]() -> TSharedRef<SWidget> {			// Open a color picker			return SNew(SColorPicker)				.TargetColorAttribute_Lambda(GetValue)				.UseAlpha(true)				.DisplayInlineVersion(true)				.OnColorCommitted_Lambda(SetValue);		};		return SNew(SComboButton)			.ContentPadding(0)			.HasDownArrow(false)			.ButtonStyle(FAppStyle::Get(), "Sequencer.AnimationOutliner.ColorStrip")			.OnGetMenuContent_Lambda(OnGetMenuContent)			.CollapseMenuOnParentFocus(true)			.ButtonContent()			[				SNew(SColorBlock)				.Color_Lambda(GetValue)				.ShowBackgroundForAlpha(true)				.AlphaDisplayMode(EColorBlockAlphaDisplayMode::Ignore)				.Size(FVector2D(10.0f, 10.0f))			];	}}/////////////////////////////////////////////////////////////// SExtAssetView implementation//SExtAssetView::~SExtAssetView(){	// Remove the listener for when view settings are changed	UExtContentBrowserSettings::OnSettingChanged().RemoveAll(this);	if ( FrontendFilters.IsValid() )	{		// Clear the frontend filter changed delegate		FrontendFilters->OnChanged().RemoveAll( this );	}	FExtContentBrowserSingleton::GetAssetRegistry().OnRootPathAdded().RemoveAll(this);	FExtContentBrowserSingleton::GetAssetRegistry().OnRootPathRemoved().RemoveAll(this);#if ECB_FEA_ASYNC_ASSET_DISCOVERY	FExtContentBrowserSingleton::GetAssetRegistry().OnAssetGathered().RemoveAll(this);#endif#if ECB_WIP_REPARSE_ASSET	FExtContentBrowserSingleton::GetAssetRegistry().OnAssetUpdated().RemoveAll(this);#endif	// Release all rendering resources being held onto	AssetThumbnailPool.Reset();}BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATIONvoid SExtAssetView::Construct( const FArguments& InArgs ){	bIsWorking = false;	TotalAmortizeTime = 0;	AmortizeStartTime = 0;	MaxSecondsPerFrame = 0.015;	bFillEmptySpaceInTileView = InArgs._FillEmptySpaceInTileView;	FillScale = 1.0f;	ThumbnailHintFadeInSequence.JumpToStart();	ThumbnailHintFadeInSequence.AddCurve(0, 0.5f, ECurveEaseFunction::Linear);#if ECB_LEGACY	// Load the asset registry module to listen for updates	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));	AssetRegistryModule.Get().OnAssetAdded().AddSP( this, &SExtAssetView::OnAssetAdded );	AssetRegistryModule.Get().OnAssetRemoved().AddSP( this, &SExtAssetView::OnAssetRemoved );	AssetRegistryModule.Get().OnAssetRenamed().AddSP( this, &SExtAssetView::OnAssetRenamed );	AssetRegistryModule.Get().OnAssetUpdated().AddSP( this, &SExtAssetView::OnAssetUpdated );	AssetRegistryModule.Get().OnPathAdded().AddSP( this, &SExtAssetView::OnAssetRegistryPathAdded );	AssetRegistryModule.Get().OnPathRemoved().AddSP( this, &SExtAssetView::OnAssetRegistryPathRemoved );#endif#if ECB_FEA_ASYNC_ASSET_DISCOVERY	FExtContentBrowserSingleton::GetAssetRegistry().OnAssetGathered().AddSP(this, &SExtAssetView::OnAssetRegistryAssetGathered);#endif#if ECB_WIP_REPARSE_ASSET	FExtContentBrowserSingleton::GetAssetRegistry().OnAssetUpdated().AddSP(this, &SExtAssetView::OnAssetUpdated);#endif	FExtContentBrowserSingleton::GetAssetRegistry().OnRootPathAdded().AddSP(this, &SExtAssetView::OnAssetRegistryRootPathAdded);	FExtContentBrowserSingleton::GetAssetRegistry().OnRootPathRemoved().AddSP(this, &SExtAssetView::OnAssetRegistryRootPathRemoved);#if ECB_WIP_COLLECTION	FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();	CollectionManagerModule.Get().OnAssetsAdded().AddSP( this, &SExtAssetView::OnAssetsAddedToCollection );	CollectionManagerModule.Get().OnAssetsRemoved().AddSP( this, &SExtAssetView::OnAssetsRemovedFromCollection );	CollectionManagerModule.Get().OnCollectionRenamed().AddSP( this, &SExtAssetView::OnCollectionRenamed );	CollectionManagerModule.Get().OnCollectionUpdated().AddSP( this, &SExtAssetView::OnCollectionUpdated );#endif#if ECB_LEGACY	// Listen for when assets are loaded or changed to update item data	FCoreUObjectDelegates::OnAssetLoaded.AddSP(this, &SExtAssetView::OnAssetLoaded);	FCoreUObjectDelegates::OnObjectPropertyChanged.AddSP(this, &SExtAssetView::OnObjectPropertyChanged);	// Listen to find out when previously empty paths are populated with content	{		TSharedRef<FEmptyFolderVisibilityManager> EmptyFolderVisibilityManager = FContentBrowserSingleton::Get().GetEmptyFolderVisibilityManager();		EmptyFolderVisibilityManager->OnFolderPopulated().AddSP(this, &SExtAssetView::OnFolderPopulated);	}#endif	// Listen for when view settings are changed	UExtContentBrowserSettings::OnSettingChanged().AddSP(this, &SExtAssetView::HandleSettingChanged);	// Get desktop metrics	FDisplayMetrics DisplayMetrics;	FSlateApplication::Get().GetCachedDisplayMetrics( DisplayMetrics );	const FVector2D DisplaySize(		DisplayMetrics.PrimaryDisplayWorkAreaRect.Right - DisplayMetrics.PrimaryDisplayWorkAreaRect.Left,		DisplayMetrics.PrimaryDisplayWorkAreaRect.Bottom - DisplayMetrics.PrimaryDisplayWorkAreaRect.Top );	const float ThumbnailScaleRangeScalar = ( DisplaySize.Y / 1080 );	// Create a thumbnail pool for rendering thumbnails		AssetThumbnailPool = MakeShareable( new FExtAssetThumbnailPool(1024, /*InAreRealTimeThumbnailsAllowed = */ false) );	NumOffscreenThumbnails = 64;	ListViewThumbnailResolution = 128;	ListViewThumbnailSize = 64;	ListViewThumbnailPadding = 4;	TileViewThumbnailResolution = 256;	TileViewThumbnailSize = 128;	TileViewThumbnailPadding = 5;	TileViewNameHeight = 36;	ThumbnailScaleSliderValue = InArgs._ThumbnailScale; 	if ( !ThumbnailScaleSliderValue.IsBound() )	{		ThumbnailScaleSliderValue = FMath::Clamp<float>(ThumbnailScaleSliderValue.Get(), 0.0f, 1.0f);	}	MinThumbnailScale = 0.2f * ThumbnailScaleRangeScalar;	MaxThumbnailScale = 2.0f * ThumbnailScaleRangeScalar;	bCanShowFolders = InArgs._CanShowFolders;	bFilterRecursivelyWithBackendFilter = InArgs._FilterRecursivelyWithBackendFilter;	bCanShowCollections = InArgs._CanShowCollections;	bCanShowFavorites = InArgs._CanShowFavorites;#if ECB_TODO	bPreloadAssetsForContextMenu = InArgs._PreloadAssetsForContextMenu;#endif	SelectionMode = InArgs._SelectionMode;	bShowMajorAssetTypeColumnsInColumnView = InArgs._ShowMajorAssetTypeColumnsInColumnView;	bShowPathInColumnView = InArgs._ShowPathInColumnView;	bShowTypeInColumnView = InArgs._ShowTypeInColumnView;	bSortByPathInColumnView = bShowPathInColumnView & InArgs._SortByPathInColumnView;	bPendingUpdateThumbnails = false;	bShouldNotifyNextAssetSync = true;	CurrentThumbnailSize = TileViewThumbnailSize;	SourcesData = InArgs._InitialSourcesData;	BackendFilter = InArgs._InitialBackendFilter;	FrontendFilters = InArgs._FrontendFilters;	if ( FrontendFilters.IsValid() )	{		FrontendFilters->OnChanged().AddSP( this, &SExtAssetView::OnFrontendFiltersChanged );	}	OnShouldFilterAsset = InArgs._OnShouldFilterAsset;	OnAssetSelected = InArgs._OnAssetSelected;	OnAssetsActivated = InArgs._OnAssetsActivated;	OnGetAssetContextMenu = InArgs._OnGetAssetContextMenu;#if ECB_LEGACY	OnGetFolderContextMenu = InArgs._OnGetFolderContextMenu;	OnGetPathContextMenuExtender = InArgs._OnGetPathContextMenuExtender;	OnFindInAssetTreeRequested = InArgs._OnFindInAssetTreeRequested;	OnAssetRenameCommitted = InArgs._OnAssetRenameCommitted;	OnAssetTagWantsToBeDisplayed = InArgs._OnAssetTagWantsToBeDisplayed;	OnIsAssetValidForCustomToolTip = InArgs._OnIsAssetValidForCustomToolTip;	OnGetCustomAssetToolTip = InArgs._OnGetCustomAssetToolTip;	OnVisualizeAssetToolTip = InArgs._OnVisualizeAssetToolTip;	OnAssetToolTipClosing = InArgs._OnAssetToolTipClosing;	OnGetCustomSourceAssets = InArgs._OnGetCustomSourceAssets;#endif	HighlightedText = InArgs._HighlightedText;	ThumbnailLabel = InArgs._ThumbnailLabel;	AllowThumbnailHintLabel = InArgs._AllowThumbnailHintLabel;	AssetShowWarningText = InArgs._AssetShowWarningText;	bAllowDragging = InArgs._AllowDragging;#if ECB_LEGACY	bAllowFocusOnSync = InArgs._AllowFocusOnSync;#endif	OnPathSelected = InArgs._OnPathSelected;	HiddenColumnNames = DefaultHiddenColumnNames = InArgs._HiddenColumnNames;	CustomColumns = InArgs._CustomColumns;	OnSearchOptionsChanged = InArgs._OnSearchOptionsChanged;	OnRequestShowChangeLog = InArgs._OnRequestShowChangeLog;	if ( InArgs._InitialViewType >= 0 && InArgs._InitialViewType < EAssetViewType::MAX )	{		CurrentViewType = InArgs._InitialViewType;	}	else	{		CurrentViewType = EAssetViewType::Tile;	}#if ECB_TODO	bPendingSortFilteredItems = false;	bQuickFrontendListRefreshRequested = false;	bSlowFullListRefreshRequested = false;	LastSortTime = 0;	SortDelaySeconds = 8;	LastProcessAddsTime = 0;#endif	bBulkSelecting = false;	bUserSearching = false;	bPendingFocusOnSync = false;	bWereItemsRecursivelyFiltered = false;	NumVisibleColumns = 0;#if ECB_LEGACY	FEditorWidgetsModule& EditorWidgetsModule = FModuleManager::LoadModuleChecked<FEditorWidgetsModule>("EditorWidgets");	TSharedRef<SWidget> AssetDiscoveryIndicator = EditorWidgetsModule.CreateAssetDiscoveryIndicator(EAssetDiscoveryIndicatorScaleMode::Scale_Vertical);#endif	TSharedRef<SVerticalBox> VerticalBox = SNew(SVerticalBox);	ChildSlot	[		VerticalBox	];	// Assets area	VerticalBox->AddSlot()	.FillHeight(1.f)	[		SNew( SVerticalBox ) 		// Working Bar >>#if ECB_FOLD 		+ SVerticalBox::Slot()		.AutoHeight()		[			SNew( SBox )			.Visibility_Lambda([this] { return bIsWorking ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed; })			.HeightOverride( 2 )			[				SNew( SProgressBar )				.Percent( this, &SExtAssetView::GetIsWorkingProgressBarState )				.BorderPadding( FVector2D(0,0) )			]		]#endif	// Working Bar <<		+ SVerticalBox::Slot()		.FillHeight(1.f)		[			SNew(SOverlay)			+ SOverlay::Slot()			.HAlign(HAlign_Fill)			.VAlign(VAlign_Fill)			[				// Container for the view types				SAssignNew(ViewContainer, SBorder)				.Padding(0)			]			+ SOverlay::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Center).Padding(FMargin(0, 14, 0, 0))			[				// A warning to display when there are no assets to show				SNew( STextBlock ).Justification( ETextJustify::Center ).AutoWrapText(true)				.Text( this, &SExtAssetView::GetAssetShowWarningText )				.Visibility( this, &SExtAssetView::IsAssetShowWarningTextVisible )			]#if 0 //ECB_WIP_ASYNC_ASSET_DISCOVERY			+ SOverlay::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Bottom).Padding(FMargin(24, 0, 24, 0))			[				// Asset discovery indicator				SNew(SExtAssetDiscoveryIndicator)				.ScaleMode(EAssetDiscoveryIndicatorScaleMode::Scale_None)				.Padding(FMargin(2))				.FadeIn(false/*bFadeIn*/)			]#endif#if ECB_LEGACY			+ SOverlay::Slot()			.HAlign(HAlign_Right)			.VAlign(VAlign_Bottom)			.Padding(FMargin(8, 0))			[				SNew(SBorder)				.BorderImage(FAppStyle::GetBrush("ErrorReporting.EmptyBox"))				.BorderBackgroundColor(this, &SExtAssetView::GetQuickJumpColor)				.Visibility(this, &SExtAssetView::IsQuickJumpVisible)				[					SNew(STextBlock)					.Text(this, &SExtAssetView::GetQuickJumpTerm)				]			]#endif		]	];	if (InArgs._ShowBottomToolbar)	{		// Bottom panel		VerticalBox->AddSlot().AutoHeight()		[			SNew(SHorizontalBox)			// Asset count			+SHorizontalBox::Slot().FillWidth(1.f).VAlign(VAlign_Center).Padding(8, 0)			[				SNew(SHorizontalBox)				+ SHorizontalBox::Slot().AutoWidth().VAlign(VAlign_Center).Padding(0, 0)				[					SNew(STextBlock).Text(this, &SExtAssetView::GetAssetCountText)				]#if ECB_FEA_ASYNC_ASSET_DISCOVERY				+ SHorizontalBox::Slot().AutoWidth().VAlign(VAlign_Center).Padding(4, 0)				[					SNew(SExtAssetDiscoveryIndicator)					.ScaleMode(EAssetDiscoveryIndicatorScaleMode::Scale_None)					.Padding(FMargin(2))					.FadeIn(true/*bFadeIn*/)				]#endif			]			// View mode combo button			+SHorizontalBox::Slot().AutoWidth()			[				SNew(SHorizontalBox)				// Thumbnail Scale				+ SHorizontalBox::Slot().AutoWidth().VAlign(VAlign_Center).Padding(4, 0)				[					SAssignNew(ThumbnailScaleContainer, SHorizontalBox)					+ SHorizontalBox::Slot().AutoWidth().Padding(0, 0, 2, 0)					.VAlign(VAlign_Center)					[						SNew(STextBlock).Text(LOCTEXT("ThumbnailScale", "Thumbnail Scale"))					]					+ SHorizontalBox::Slot().AutoWidth().VAlign(VAlign_Center)					[						SNew(SBox)						.WidthOverride(120.0f)						[							SNew(SSlider)							.ToolTipText(LOCTEXT("ThumbnailScaleToolTip", "Adjust the size of thumbnails."))							.Value(this, &SExtAssetView::GetThumbnailScale)							.OnValueChanged(this, &SExtAssetView::SetThumbnailScale)							.Locked(this, &SExtAssetView::IsThumbnailScalingLocked)						]					]				]				// View options				+SHorizontalBox::Slot().AutoWidth()				[					SAssignNew(ViewOptionsComboButton, SComboButton)					.ContentPadding(0)					.ForegroundColor(this, &SExtAssetView::GetViewButtonForegroundColor)					.ButtonStyle(FAppStyle::Get(), "ToggleButton") // Use the tool bar item style for this button					.OnGetMenuContent(this, &SExtAssetView::GetViewButtonContent)					.ButtonContent()					[						SNew(SHorizontalBox)						+ SHorizontalBox::Slot()						.AutoWidth()						.VAlign(VAlign_Center)						[							SNew(SImage).Image(FAppStyle::GetBrush("GenericViewButton"))						]						+ SHorizontalBox::Slot()						.AutoWidth()						.Padding(2, 0, 0, 0)						.VAlign(VAlign_Center)						[							SNew(STextBlock).Text(LOCTEXT("ViewButton", "View Options"))						]					]				]			]		];	}	CreateCurrentView();#if ECB_WIP_INITIAL_ASSET	if( InArgs._InitialAssetSelection.IsValid() )	{		// sync to the initial item without notifying of selection		bShouldNotifyNextAssetSync = false;		TArray<FAssetData> AssetsToSync;		AssetsToSync.Add( InArgs._InitialAssetSelection );		SyncToAssets( AssetsToSync );	}#endif#if ECB_WIP_MORE_VIEWTYPE	// If currently looking at column, and you could choose to sort by path in column first and then name	// Generalizing this is a bit difficult because the column ID is not accessible or is not known	// Currently I assume this won't work, if this view mode is not column. Otherwise, I don't think sorting by path	// is a good idea. 	if (CurrentViewType == EAssetViewType::Column && bSortByPathInColumnView)	{		SortManager.SetSortColumnId(EColumnSortPriority::Primary, SortManager.PathColumnId);		SortManager.SetSortColumnId(EColumnSortPriority::Secondary, SortManager.NameColumnId);		SortManager.SetSortMode(EColumnSortPriority::Primary, EColumnSortMode::Ascending);		SortManager.SetSortMode(EColumnSortPriority::Secondary, EColumnSortMode::Ascending);		SortList();	}#endif#if ECB_FEA_ASSET_DRAG_DROP	static bool bInitAssetViewDragAndDropExtenders = false;	if (!bInitAssetViewDragAndDropExtenders)	{		FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>("ContentBrowser");		TArray<FAssetViewDragAndDropExtender>& AssetViewDragAndDropExtenders = ContentBrowserModule.GetAssetViewDragAndDropExtenders();		FAssetViewDragAndDropExtender DragAndDropExtender(			FAssetViewDragAndDropExtender::FOnDropDelegate::CreateSP(this, &SExtAssetView::OnDropAndDropToAssetView)			, FAssetViewDragAndDropExtender::FOnDragOverDelegate::CreateLambda([](const FAssetViewDragAndDropExtender::FPayload&) {return true; })			, FAssetViewDragAndDropExtender::FOnDragLeaveDelegate::CreateLambda([](const FAssetViewDragAndDropExtender::FPayload&) {return true; })		);		AssetViewDragAndDropExtenders.Add(DragAndDropExtender);		bInitAssetViewDragAndDropExtenders = true;	}	static bool bInitLevelViewportDragAndDropExtenders = false;	if (!bInitLevelViewportDragAndDropExtenders)	{		// Get all menu extenders for this context menu from the level editor module		static const FName LevelEditorName("LevelEditor");		FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>(LevelEditorName);		TArray<FLevelEditorModule::FLevelViewportMenuExtender_SelectedActors>& MenuExtenders = LevelEditorModule.GetAllLevelViewportContextMenuExtenders();		MenuExtenders.Add(FLevelEditorModule::FLevelViewportMenuExtender_SelectedActors::CreateSP(this, &SExtAssetView::OnExtendLevelViewportMenu));		//FDelegateHandle LevelViewportContextMenuExtenderDelegateHandle = ContextMenuExtenders.Last().GetHandle();	}#endif}END_SLATE_FUNCTION_BUILD_OPTIMIZATIONTOptional< float > SExtAssetView::GetIsWorkingProgressBarState() const{	return bIsWorking ? TOptional< float >() : 0.0f; }void SExtAssetView::SetSourcesData(const FSourcesData& InSourcesData){	// Update the path and collection lists	SourcesData = InSourcesData;	RequestSlowFullListRefresh();	ClearSelection();}const FSourcesData& SExtAssetView::GetSourcesData() const{	return SourcesData;}#if ECB_TODObool SExtAssetView::IsAssetPathSelected() const{	int32 NumAssetPaths, NumClassPaths;	ContentBrowserUtils::CountPathTypes(SourcesData.PackagePaths, NumAssetPaths, NumClassPaths);	// Check that only asset paths are selected	return NumAssetPaths > 0 && NumClassPaths == 0;}#endifvoid SExtAssetView::SetBackendFilter(const FARFilter& InBackendFilter){	// Update the path and collection lists	BackendFilter = InBackendFilter;	RequestSlowFullListRefresh();}#if ECB_TODOvoid SExtAssetView::RenameFolder(const FString& FolderToRename){	for ( auto ItemIt = FilteredAssetItems.CreateConstIterator(); ItemIt; ++ItemIt )	{		const TSharedPtr<FExtAssetViewItem>& Item = *ItemIt;		if ( Item.IsValid() && Item->GetType() == EAssetItemType::Folder )			{			const TSharedPtr<FAssetViewFolder>& ItemAsFolder = StaticCastSharedPtr<FAssetViewFolder>(Item);			if ( ItemAsFolder->FolderPath == FolderToRename )			{				ItemAsFolder->bRenameWhenScrolledIntoview = true;				SetSelection(Item);				RequestScrollIntoView(Item);				break;			}		}	}}void SExtAssetView::SyncToAssets( const TArray<FAssetData>& AssetDataList, const bool bFocusOnSync ){	PendingSyncItems.Reset();	for (const FAssetData& AssetData : AssetDataList)	{		PendingSyncItems.SelectedAssets.Add(AssetData.ObjectPath);	}	bPendingFocusOnSync = bFocusOnSync;}void SExtAssetView::SyncToFolders(const TArray<FString>& FolderList, const bool bFocusOnSync){	PendingSyncItems.Reset();	PendingSyncItems.SelectedFolders = TSet<FString>(FolderList);	bPendingFocusOnSync = bFocusOnSync;}void SExtAssetView::SyncTo(const FExtContentBrowserSelection& ItemSelection, const bool bFocusOnSync){	PendingSyncItems.Reset();	PendingSyncItems.SelectedFolders = TSet<FString>(ItemSelection.SelectedFolders);	for (const FAssetData& AssetData : ItemSelection.SelectedAssets)	{		PendingSyncItems.SelectedAssets.Add(AssetData.ObjectPath);	}	bPendingFocusOnSync = bFocusOnSync;}#endifvoid SExtAssetView::SyncToSelection( const bool bFocusOnSync ){	PendingSyncItems.Reset();	TArray<TSharedPtr<FExtAssetViewItem>> SelectedItems = GetSelectedItems();	for (const TSharedPtr<FExtAssetViewItem>& Item : SelectedItems)	{		if (Item.IsValid())		{			if (Item->GetType() == EExtAssetItemType::Folder)			{				PendingSyncItems.SelectedFolders.Add(StaticCastSharedPtr<FExtAssetViewFolder>(Item)->FolderPath);			}			else			{				PendingSyncItems.SelectedAssets.Add(StaticCastSharedPtr<FExtAssetViewAsset>(Item)->Data.ObjectPath);			}		}	}	bPendingFocusOnSync = bFocusOnSync;}#if ECB_WIP_HISTORYvoid SExtAssetView::ApplyHistoryData( const FHistoryData& History ){	SetSourcesData(History.SourcesData);	PendingSyncItems = History.SelectionData;	bPendingFocusOnSync = true;}#endifTArray<TSharedPtr<FExtAssetViewItem>> SExtAssetView::GetSelectedItems() const{	switch ( GetCurrentViewType() )	{		case EAssetViewType::List: return ListView->GetSelectedItems();		case EAssetViewType::Tile: return TileView->GetSelectedItems();		case EAssetViewType::Column: return ColumnView->GetSelectedItems();		default:		ensure(0); // Unknown list type		return TArray<TSharedPtr<FExtAssetViewItem>>();	}}TArray<FExtAssetData> SExtAssetView::GetSelectedAssets() const{	TArray<TSharedPtr<FExtAssetViewItem>> SelectedItems = GetSelectedItems();	TArray<FExtAssetData> SelectedAssets;	for ( auto ItemIt = SelectedItems.CreateConstIterator(); ItemIt; ++ItemIt )	{		const TSharedPtr<FExtAssetViewItem>& Item = *ItemIt;		// Only report non-temporary & non-folder items		if ( Item.IsValid() && !Item->IsTemporaryItem() && Item->GetType() != EExtAssetItemType::Folder )			{			SelectedAssets.Add(StaticCastSharedPtr<FExtAssetViewAsset>(Item)->Data);		}	}	return SelectedAssets;}int32 SExtAssetView::GetNumSelectedAssets() const{	int32 Num = 0;	TArray<TSharedPtr<FExtAssetViewItem>> SelectedItems = GetSelectedItems();	for (auto ItemIt = SelectedItems.CreateConstIterator(); ItemIt; ++ItemIt)	{		const TSharedPtr<FExtAssetViewItem>& Item = *ItemIt;		// Only report non-temporary & non-folder items		if (Item.IsValid() && !Item->IsTemporaryItem() && Item->GetType() != EExtAssetItemType::Folder)		{			++Num;		}	}	return Num;}TArray<FString> SExtAssetView::GetSelectedFolders() const{	TArray<TSharedPtr<FExtAssetViewItem>> SelectedItems = GetSelectedItems();	TArray<FString> SelectedFolders;	for ( auto ItemIt = SelectedItems.CreateConstIterator(); ItemIt; ++ItemIt )	{		const TSharedPtr<FExtAssetViewItem>& Item = *ItemIt;		if ( Item.IsValid() && Item->GetType() == EExtAssetItemType::Folder )			{			SelectedFolders.Add(StaticCastSharedPtr<FExtAssetViewFolder>(Item)->FolderPath);		}	}	return SelectedFolders;}void SExtAssetView::RequestSlowFullListRefresh(){	bSlowFullListRefreshRequested = true;	// Prefetch assets	{		const bool bRecurse = ShouldFilterRecursively();		const bool bUsingFolders = IsShowingFolders();		FARFilter Filter = SourcesData.MakeFilter(bRecurse, bUsingFolders);				// Add the backend filters from the filter list		Filter.Append(BackendFilter);#if ECB_FEA_ASYNC_ASSET_DISCOVERY				//FExtContentBrowserSingleton::GetAssetRegistry().CacheAssetsAsync(Filter);#else		FExtContentBrowserSingleton::GetAssetRegistry().CacheAssets(Filter);#endif	}}void SExtAssetView::RequestQuickFrontendListRefresh(){	bQuickFrontendListRefreshRequested = true;}FString SExtAssetView::GetThumbnailScaleSettingPath(const FString& SettingsString) const{	return SettingsString + TEXT(".ThumbnailSizeScale");}FString SExtAssetView::GetCurrentViewTypeSettingPath(const FString& SettingsString) const{	return SettingsString + TEXT(".CurrentViewType");}void SExtAssetView::SaveSettings(const FString& IniFilename, const FString& IniSection, const FString& SettingsString) const{	GConfig->SetFloat(*IniSection, *GetThumbnailScaleSettingPath(SettingsString), ThumbnailScaleSliderValue.Get(), IniFilename);#if ECB_WIP_MORE_VIEWTYPE	GConfig->SetInt(*IniSection, *GetCurrentViewTypeSettingPath(SettingsString), CurrentViewType, IniFilename);#endif		GConfig->SetArray(*IniSection, *(SettingsString + TEXT(".HiddenColumns")), HiddenColumnNames, IniFilename);}void SExtAssetView::LoadSettings(const FString& IniFilename, const FString& IniSection, const FString& SettingsString){	float Scale = 0.f;	if ( GConfig->GetFloat(*IniSection, *GetThumbnailScaleSettingPath(SettingsString), Scale, IniFilename) )	{		// Clamp value to normal range and update state		Scale = FMath::Clamp<float>(Scale, 0.f, 1.f);		SetThumbnailScale(Scale);	}#if ECB_WIP_MORE_VIEWTYPE	int32 ViewType = EAssetViewType::Tile;	if ( GConfig->GetInt(*IniSection, *GetCurrentViewTypeSettingPath(SettingsString), ViewType, IniFilename) )	{		// Clamp value to normal range and update state		if ( ViewType < 0 || ViewType >= EAssetViewType::MAX)		{			ViewType = EAssetViewType::Tile;		}		SetCurrentViewType( (EAssetViewType::Type)ViewType );	}	TArray<FString> LoadedHiddenColumnNames;	GConfig->GetArray(*IniSection, *(SettingsString + TEXT(".HiddenColumns")), LoadedHiddenColumnNames, IniFilename);	if (LoadedHiddenColumnNames.Num() > 0)	{		HiddenColumnNames = LoadedHiddenColumnNames;	}#endif}#if ECB_TODO// Adjusts the selected asset by the selection delta, which should be +1 or -1)void SExtAssetView::AdjustActiveSelection(int32 SelectionDelta){	// Find the index of the first selected item	TArray<TSharedPtr<FExtAssetViewItem>> SelectionSet = GetSelectedItems();		int32 SelectedSuggestion = INDEX_NONE;	if (SelectionSet.Num() > 0)	{		if (!FilteredAssetItems.Find(SelectionSet[0], /*out*/ SelectedSuggestion))		{			// Should never happen			ensureMsgf(false, TEXT("SExtAssetView has a selected item that wasn't in the filtered list"));			return;		}	}	else	{		SelectedSuggestion = 0;		SelectionDelta = 0;	}	if (FilteredAssetItems.Num() > 0)	{		// Move up or down one, wrapping around		SelectedSuggestion = (SelectedSuggestion + SelectionDelta + FilteredAssetItems.Num()) % FilteredAssetItems.Num();		// Pick the new asset		const TSharedPtr<FExtAssetViewItem>& NewSelection = FilteredAssetItems[SelectedSuggestion];		RequestScrollIntoView(NewSelection);		SetSelection(NewSelection);	}	else	{		ClearSelection();	}}#endif#if ECB_FEA_ASYNC_ASSET_DISCOVERYvoid SExtAssetView::ProcessRecentlyLoadedOrChangedAssets(){	for (int32 AssetIdx = FilteredAssetItems.Num() - 1; AssetIdx >= 0 && RecentlyLoadedOrChangedAssets.Num() > 0; --AssetIdx)	{		if (FilteredAssetItems[AssetIdx]->GetType() != EExtAssetItemType::Folder)		{			const TSharedPtr<FExtAssetViewAsset>& ItemAsAsset = StaticCastSharedPtr<FExtAssetViewAsset>(FilteredAssetItems[AssetIdx]);							// Find the updated version of the asset data from the set			// This is the version of the data we should use to update our view			FExtAssetData RecentlyLoadedOrChangedAsset;			if (const FExtAssetData* RecentlyLoadedOrChangedAssetPtr = RecentlyLoadedOrChangedAssets.Find(ItemAsAsset->Data))			{				RecentlyLoadedOrChangedAsset = *RecentlyLoadedOrChangedAssetPtr;				RecentlyLoadedOrChangedAssets.Remove(ItemAsAsset->Data);			}			if (RecentlyLoadedOrChangedAsset.IsValid() || RecentlyLoadedOrChangedAsset.IsParsed())			{				bool bShouldRemoveAsset = false;				if (!PassesCurrentBackendFilter(RecentlyLoadedOrChangedAsset))				{					bShouldRemoveAsset = true;				}				if (!bShouldRemoveAsset && OnShouldFilterAsset.IsBound() && OnShouldFilterAsset.Execute(RecentlyLoadedOrChangedAsset))				{					bShouldRemoveAsset = true;				}				if (!bShouldRemoveAsset && (IsFrontendFilterActive() && !PassesCurrentFrontendFilter(RecentlyLoadedOrChangedAsset)))				{					bShouldRemoveAsset = true;				}				if (bShouldRemoveAsset)				{					FilteredAssetItems.RemoveAt(AssetIdx);				}				else				{					// Update the asset data on the item					ItemAsAsset->SetAssetData(RecentlyLoadedOrChangedAsset);					// Update the custom column data					ItemAsAsset->CacheCustomColumns(CustomColumns, true, true, true);				}				RefreshList();			}		}	}	if (FilteredRecentlyAddedAssets.Num() == 0 && RecentlyAddedAssets.Num() == 0)	{		//No more assets coming in so if we haven't found them now we aren't going to		RecentlyLoadedOrChangedAssets.Reset();	}}#endifvoid SExtAssetView::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ){	CalculateFillScale( AllottedGeometry );	CurrentTime = InCurrentTime;	static float WidthLastFrame = 0.f;	if (WidthLastFrame != AllottedGeometry.Size.X)	{		ECB_LOG(Display, TEXT("AssetView:WidthLastFrame: %.2f"), WidthLastFrame);		WidthLastFrame = AllottedGeometry.Size.X;	}	{#if ECB_FEA_ASYNC_ASSET_DISCOVERY		bool bIsGatheringAsset = FExtContentBrowserSingleton::GetAssetRegistry().IsGatheringAssets();#else		bool bIsGatheringAsset = false;#endif		ThumbnailScaleContainer->SetVisibility(WidthLastFrame < (bIsGatheringAsset ? 609.f : 372.f) ? EVisibility::Collapsed : EVisibility::Visible);		ViewOptionsComboButton->SetVisibility(WidthLastFrame < (bIsGatheringAsset ? 405.f : 150.f) ? EVisibility::Collapsed : EVisibility::Visible);	}	#if ECB_FEA_ASYNC_ASSET_DISCOVERY		if (FExtContentBrowserSingleton::GetAssetRegistry().GetAndTrimAssetGatherResult())	{		//bSlowFullListRefreshRequested = true;	}	// If there were any assets that were recently added via the asset registry, process them now	ProcessRecentlyAddedAssets();	// If there were any assets loaded since last frame that we are currently displaying thumbnails for, push them on the render stack now.	ProcessRecentlyLoadedOrChangedAssets();#endif	CalculateThumbnailHintColorAndOpacity();	if (FSlateApplication::Get().GetActiveModalWindow().IsValid())	{		// If we're in a model window then we need to tick the thumbnail pool in order for thumbnails to render correctly.		AssetThumbnailPool->Tick(InDeltaTime);	}	if ( bPendingUpdateThumbnails )	{		UpdateThumbnails();		bPendingUpdateThumbnails = false;	}	if (bSlowFullListRefreshRequested)	{		RefreshSourceItems();		bSlowFullListRefreshRequested = false;		bQuickFrontendListRefreshRequested = true;	}	if (QueriedAssetItems.Num() > 0)	{		check(OnShouldFilterAsset.IsBound());		double TickStartTime = FPlatformTime::Seconds();		// Mark the first amortize time		if (AmortizeStartTime == 0)		{			AmortizeStartTime = FPlatformTime::Seconds();			bIsWorking = true;		}		ProcessQueriedItems(TickStartTime);		if (QueriedAssetItems.Num() == 0)		{			TotalAmortizeTime += FPlatformTime::Seconds() - AmortizeStartTime;			AmortizeStartTime = 0;			bIsWorking = false;		}		else		{			// Need to finish processing queried items before rest of function is safe			return;		}	}	if (bQuickFrontendListRefreshRequested)	{		//ResetQuickJump();				RefreshFilteredItems();		RefreshFolders();		SortList(!PendingSyncItems.Num()); // Don't sync to selection if we are just going to do it below		bQuickFrontendListRefreshRequested = false;	}	if ( PendingSyncItems.Num() > 0 )	{		if (bPendingSortFilteredItems)		{			// Don't sync to selection because we are just going to do it below			SortList(/*bSyncToSelection=*/false);		}				bBulkSelecting = true;		ClearSelection();		bool bFoundScrollIntoViewTarget = false;		for ( auto ItemIt = FilteredAssetItems.CreateConstIterator(); ItemIt; ++ItemIt )		{			const auto& Item = *ItemIt;			if(Item.IsValid())			{				if(Item->GetType() == EExtAssetItemType::Folder)				{					const TSharedPtr<FExtAssetViewFolder>& ItemAsFolder = StaticCastSharedPtr<FExtAssetViewFolder>(Item);					if ( PendingSyncItems.SelectedFolders.Contains(ItemAsFolder->FolderPath) )					{						SetItemSelection(*ItemIt, true, ESelectInfo::OnNavigation);						// Scroll the first item in the list that can be shown into view						if ( !bFoundScrollIntoViewTarget )						{							RequestScrollIntoView(Item);							bFoundScrollIntoViewTarget = true;						}					}				}				else				{					const TSharedPtr<FExtAssetViewAsset>& ItemAsAsset = StaticCastSharedPtr<FExtAssetViewAsset>(Item);					if ( PendingSyncItems.SelectedAssets.Contains(ItemAsAsset->Data.ObjectPath) )					{						SetItemSelection(*ItemIt, true, ESelectInfo::OnNavigation);						// Scroll the first item in the list that can be shown into view						if ( !bFoundScrollIntoViewTarget )						{							RequestScrollIntoView(Item);							bFoundScrollIntoViewTarget = true;						}					}				}			}		}			bBulkSelecting = false;		if (bShouldNotifyNextAssetSync && !bUserSearching)		{			AssetSelectionChanged(TSharedPtr<FExtAssetViewAsset>(), ESelectInfo::Direct);		}		// Default to always notifying		bShouldNotifyNextAssetSync = true;		PendingSyncItems.Reset();		if (bAllowFocusOnSync && bPendingFocusOnSync)		{			FocusList();		}	}	if ( IsHovered() )	{		// This prevents us from sorting the view immediately after the cursor leaves it		LastSortTime = CurrentTime;	}	else if ( bPendingSortFilteredItems && InCurrentTime > LastSortTime + SortDelaySeconds )	{		SortList();	}#if ECB_WIP_BREADCRUMB	// Do quick-jump last as the Tick function might have canceled it	if(QuickJumpData.bHasChangedSinceLastTick)	{		QuickJumpData.bHasChangedSinceLastTick = false;		const bool bWasJumping = QuickJumpData.bIsJumping;		QuickJumpData.bIsJumping = true;		QuickJumpData.LastJumpTime = InCurrentTime;		QuickJumpData.bHasValidMatch = PerformQuickJump(bWasJumping);	}	else if(QuickJumpData.bIsJumping && InCurrentTime > QuickJumpData.LastJumpTime + JumpDelaySeconds)	{		ResetQuickJump();	}	TSharedPtr<FExtAssetViewItem> AssetAwaitingRename = AwaitingRename.Pin();	if (AssetAwaitingRename.IsValid())	{		TSharedPtr<SWindow> OwnerWindow = FSlateApplication::Get().FindWidgetWindow(AsShared());		if (!OwnerWindow.IsValid())		{			AssetAwaitingRename->bRenameWhenScrolledIntoview = false;			AwaitingRename = nullptr;		}		else if (OwnerWindow->HasAnyUserFocusOrFocusedDescendants())		{			AssetAwaitingRename->RenamedRequestEvent.ExecuteIfBound();			AssetAwaitingRename->bRenameWhenScrolledIntoview = false;			AwaitingRename = nullptr;		}	}#endif}void SExtAssetView::CalculateFillScale( const FGeometry& AllottedGeometry ){	if ( bFillEmptySpaceInTileView && CurrentViewType == EAssetViewType::Tile )	{		float ItemWidth = GetTileViewItemBaseWidth();		// Scrollbars are 16, but we add 1 to deal with half pixels.		const float ScrollbarWidth = 16 + 1;		float TotalWidth = AllottedGeometry.GetLocalSize().X - ( ScrollbarWidth / AllottedGeometry.Scale );		float Coverage = TotalWidth / ItemWidth;		int32 Items = (int)( TotalWidth / ItemWidth );		// If there isn't enough room to support even a single item, don't apply a fill scale.		if ( Items > 0 )		{			float GapSpace = ItemWidth * ( Coverage - Items );			float ExpandAmount = GapSpace / (float)Items;			FillScale = ( ItemWidth + ExpandAmount ) / ItemWidth;			FillScale = FMath::Max( 1.0f, FillScale );		}		else		{			FillScale = 1.0f;		}	}	else	{		FillScale = 1.0f;	}}void SExtAssetView::CalculateThumbnailHintColorAndOpacity(){	if ( HighlightedText.Get().IsEmpty() )	{		if ( ThumbnailHintFadeInSequence.IsPlaying() )		{			if ( ThumbnailHintFadeInSequence.IsForward() )			{				ThumbnailHintFadeInSequence.Reverse();			}		}		else if ( ThumbnailHintFadeInSequence.IsAtEnd() ) 		{			ThumbnailHintFadeInSequence.PlayReverse(this->AsShared());		}	}	else 	{		if ( ThumbnailHintFadeInSequence.IsPlaying() )		{			if ( ThumbnailHintFadeInSequence.IsInReverse() )			{				ThumbnailHintFadeInSequence.Reverse();			}		}		else if ( ThumbnailHintFadeInSequence.IsAtStart() ) 		{			ThumbnailHintFadeInSequence.Play(this->AsShared());		}	}	const float Opacity = ThumbnailHintFadeInSequence.GetLerp();	ThumbnailHintColorAndOpacity = FLinearColor( 1.0, 1.0, 1.0, Opacity );}void SExtAssetView::ProcessQueriedItems( const double TickStartTime ){	const bool bFlushFullBuffer = TickStartTime < 0;	bool ListNeedsRefresh = false;	int32 AssetIndex = 0;	for ( AssetIndex = QueriedAssetItems.Num() - 1; AssetIndex >= 0 ; AssetIndex--)	{		if ( !OnShouldFilterAsset.Execute( QueriedAssetItems[AssetIndex] ) )		{			AssetItems.Add( QueriedAssetItems[AssetIndex] );			if ( !IsFrontendFilterActive() || PassesCurrentFrontendFilter(QueriedAssetItems[AssetIndex]))			{				const FExtAssetData& AssetData = QueriedAssetItems[AssetIndex];				FilteredAssetItems.Add(MakeShareable(new FExtAssetViewAsset(AssetData)));				ListNeedsRefresh = true;				bPendingSortFilteredItems = true;			}		}		// Check to see if we have run out of time in this tick		if ( !bFlushFullBuffer && (FPlatformTime::Seconds() - TickStartTime) > MaxSecondsPerFrame)		{			break;		}	}	// Trim the results array	if (AssetIndex > 0)	{		QueriedAssetItems.RemoveAt( AssetIndex, QueriedAssetItems.Num() - AssetIndex );	}	else	{		QueriedAssetItems.Reset();	}	if ( ListNeedsRefresh )	{		RefreshList();	}}#if ECB_FEA_ASSET_DRAG_DROPvoid SExtAssetView::OnDragLeave( const FDragDropEvent& DragDropEvent ){#if ECB_LEGACY	TSharedPtr< FAssetDragDropOp > AssetDragDropOp = DragDropEvent.GetOperationAs< FAssetDragDropOp >();	if( AssetDragDropOp.IsValid() )	{		AssetDragDropOp->ResetToDefaultToolTip();		return;	}	TSharedPtr<FDragDropOperation> DragDropOp = DragDropEvent.GetOperation();	if (DragDropOp.IsValid())	{		// Do we have a custom handler for this drag event?		FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>("ContentBrowser");		const TArray<FAssetViewDragAndDropExtender>& AssetViewDragAndDropExtenders = ContentBrowserModule.GetAssetViewDragAndDropExtenders();		for (const auto& AssetViewDragAndDropExtender : AssetViewDragAndDropExtenders)		{			if (AssetViewDragAndDropExtender.OnDragLeaveDelegate.IsBound() && AssetViewDragAndDropExtender.OnDragLeaveDelegate.Execute(FAssetViewDragAndDropExtender::FPayload(DragDropOp, SourcesData.PackagePaths, SourcesData.Collections)))			{				return;			}		}	}#endif}FReply SExtAssetView::OnDragOver( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent ){#if ECB_LEGACY	TSharedPtr<FDragDropOperation> DragDropOp = DragDropEvent.GetOperation();	if (DragDropOp.IsValid())	{		// Do we have a custom handler for this drag event?		FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>("ContentBrowser");		const TArray<FAssetViewDragAndDropExtender>& AssetViewDragAndDropExtenders = ContentBrowserModule.GetAssetViewDragAndDropExtenders();		for (const auto& AssetViewDragAndDropExtender : AssetViewDragAndDropExtenders)		{			if (AssetViewDragAndDropExtender.OnDragOverDelegate.IsBound() && AssetViewDragAndDropExtender.OnDragOverDelegate.Execute(FAssetViewDragAndDropExtender::FPayload(DragDropOp, SourcesData.PackagePaths, SourcesData.Collections)))			{				return FReply::Handled();			}		}	}	if (SourcesData.HasPackagePaths())	{		// Note: We don't test IsAssetPathSelected here as we need to prevent dropping assets on class paths		const FString DestPath = SourcesData.PackagePaths[0].ToString();		bool bUnused = false;		DragDropHandler::ValidateDragDropOnAssetFolder(MyGeometry, DragDropEvent, DestPath, bUnused);		return FReply::Handled();	}	else if (HasSingleCollectionSource())	{		TArray< FExtAssetData > AssetDatas = AssetUtil::ExtractAssetDataFromDrag(DragDropEvent);		if (AssetDatas.Num() > 0)		{			TSharedPtr<FAssetDragDropOp> AssetDragDropOp = DragDropEvent.GetOperationAs< FAssetDragDropOp >();			if (AssetDragDropOp.IsValid())			{				TArray< FName > ObjectPaths;				FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();				const FCollectionNameType& Collection = SourcesData.Collections[0];				CollectionManagerModule.Get().GetObjectsInCollection(Collection.Name, Collection.Type, ObjectPaths);				bool IsValidDrop = false;				for (const auto& AssetData : AssetDatas)				{					if (AssetData.GetClass()->IsChildOf(UClass::StaticClass()))					{						continue;					}					if (!ObjectPaths.Contains(AssetData.ObjectPath))					{						IsValidDrop = true;						break;					}				}				if (IsValidDrop)				{					AssetDragDropOp->SetToolTip(NSLOCTEXT("AssetView", "OnDragOverCollection", "Add to Collection"), FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.OK")));				}			}			return FReply::Handled();		}	}#endif	return FReply::Unhandled();}FReply SExtAssetView::OnDrop( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent ){#if ECB_LEGACY	TSharedPtr<FDragDropOperation> DragDropOp = DragDropEvent.GetOperation();	if (DragDropOp.IsValid())	{		// Do we have a custom handler for this drag event?		FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>("ContentBrowser");		const TArray<FAssetViewDragAndDropExtender>& AssetViewDragAndDropExtenders = ContentBrowserModule.GetAssetViewDragAndDropExtenders();		for (const auto& AssetViewDragAndDropExtender : AssetViewDragAndDropExtenders)		{			if (AssetViewDragAndDropExtender.OnDropDelegate.IsBound() && AssetViewDragAndDropExtender.OnDropDelegate.Execute(FAssetViewDragAndDropExtender::FPayload(DragDropOp, SourcesData.PackagePaths, SourcesData.Collections)))			{				return FReply::Handled();			}		}	}	if (SourcesData.HasPackagePaths())	{		// Note: We don't test IsAssetPathSelected here as we need to prevent dropping assets on class paths		const FString DestPath = SourcesData.PackagePaths[0].ToString();		bool bUnused = false;		if (DragDropHandler::ValidateDragDropOnAssetFolder(MyGeometry, DragDropEvent, DestPath, bUnused))		{			// Handle drag drop for import			TSharedPtr<FExternalDragOperation> ExternalDragDropOp = DragDropEvent.GetOperationAs<FExternalDragOperation>();			if (ExternalDragDropOp.IsValid())			{				if (ExternalDragDropOp->HasFiles())				{					// Delay import until next tick to avoid blocking the process that files were dragged from					GEditor->GetEditorSubsystem<UImportSubsystem>()->ImportNextTick(ExternalDragDropOp->GetFiles(), SourcesData.PackagePaths[0].ToString());				}			}			TSharedPtr<FAssetDragDropOp> AssetDragDropOp = DragDropEvent.GetOperationAs<FAssetDragDropOp>();			if (AssetDragDropOp.IsValid())			{				OnAssetsOrPathsDragDropped(AssetDragDropOp->GetAssets(), AssetDragDropOp->GetAssetPaths(), DestPath);			}		}		return FReply::Handled();	}	else if (HasSingleCollectionSource())	{		TArray<FExtAssetData> SelectedAssetDatas = AssetUtil::ExtractAssetDataFromDrag(DragDropEvent);		if (SelectedAssetDatas.Num() > 0)		{			TSharedPtr<FAssetDragDropOp> AssetDragDropOp = DragDropEvent.GetOperationAs< FAssetDragDropOp >();			if (AssetDragDropOp.IsValid())			{				TArray<FName> ObjectPaths;				for (const auto& AssetData : SelectedAssetDatas)				{					if (!AssetData.GetClass()->IsChildOf(UClass::StaticClass()))					{						ObjectPaths.Add(AssetData.ObjectPath);					}				}				if (ObjectPaths.Num() > 0)				{					FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();					const FCollectionNameType& Collection = SourcesData.Collections[0];					CollectionManagerModule.Get().AddToCollection(Collection.Name, Collection.Type, ObjectPaths);				}				}			return FReply::Handled();		}	}#endif	return FReply::Unhandled();}#endif#if ECB_LEGACYFReply SExtAssetView::OnKeyChar( const FGeometry& MyGeometry,const FCharacterEvent& InCharacterEvent ){	const bool bIsControlOrCommandDown = InCharacterEvent.IsControlDown() || InCharacterEvent.IsCommandDown();		const bool bTestOnly = false;	if(HandleQuickJumpKeyDown(InCharacterEvent.GetCharacter(), bIsControlOrCommandDown, InCharacterEvent.IsAltDown(), bTestOnly).IsEventHandled())	{		return FReply::Handled();	}	// If the user pressed a key we couldn't handle, reset the quick-jump search	ResetQuickJump();	return FReply::Unhandled();}static bool IsValidObjectPath(const FString& Path){	int32 NameStartIndex = INDEX_NONE;	Path.FindChar(TCHAR('\''), NameStartIndex);	if (NameStartIndex != INDEX_NONE)	{		int32 NameEndIndex = INDEX_NONE;		Path.FindLastChar(TCHAR('\''), NameEndIndex);		if (NameEndIndex > NameStartIndex)		{			const FString ClassName = Path.Left(NameStartIndex);			const FString PathName = Path.Mid(NameStartIndex + 1, NameEndIndex - NameStartIndex - 1);			UClass* Class = FindObject<UClass>(ANY_PACKAGE, *ClassName, true);			if (Class)			{				return FPackageName::IsValidLongPackageName(FPackageName::ObjectPathToPackageName(PathName));			}		}	}	return false;}static bool ContainsT3D(const FString& ClipboardText){	return (ClipboardText.StartsWith(TEXT("Begin Object")) && ClipboardText.EndsWith(TEXT("End Object")))		|| (ClipboardText.StartsWith(TEXT("Begin Map")) && ClipboardText.EndsWith(TEXT("End Map")));}#endifFReply SExtAssetView::OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent ){#if ECB_LEGACY	const bool bIsControlOrCommandDown = InKeyEvent.IsControlDown() || InKeyEvent.IsCommandDown();		if (bIsControlOrCommandDown && InKeyEvent.GetCharacter() == 'V' && IsAssetPathSelected())	{		FString AssetPaths;		TArray<FString> AssetPathsSplit;		// Get the copied asset paths		FPlatformApplicationMisc::ClipboardPaste(AssetPaths);		// Make sure the clipboard does not contain T3D		AssetPaths.TrimEndInline();		if (!ContainsT3D(AssetPaths))		{			AssetPaths.ParseIntoArrayLines(AssetPathsSplit);			// Get assets and copy them			TArray<UObject*> AssetsToCopy;			for (const FString& AssetPath : AssetPathsSplit)			{				// Validate string				if (IsValidObjectPath(AssetPath))				{					UObject* ObjectToCopy = LoadObject<UObject>(nullptr, *AssetPath);					if (ObjectToCopy && !ObjectToCopy->IsA(UClass::StaticClass()))					{						AssetsToCopy.Add(ObjectToCopy);					}				}			}			if (AssetsToCopy.Num())			{				ContentBrowserUtils::CopyAssets(AssetsToCopy, SourcesData.PackagePaths[0].ToString());			}		}		return FReply::Handled();	}	// Swallow the key-presses used by the quick-jump in OnKeyChar to avoid other things (such as the viewport commands) getting them instead	// eg) Pressing "W" without this would set the viewport to "translate" mode	else if(HandleQuickJumpKeyDown(InKeyEvent.GetCharacter(), bIsControlOrCommandDown, InKeyEvent.IsAltDown(), /*bTestOnly*/true).IsEventHandled())	{		return FReply::Handled();	}#endif	return FReply::Unhandled();}FReply SExtAssetView::OnMouseWheel( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ){	if( MouseEvent.IsControlDown() )	{		const float DesiredScale = FMath::Clamp<float>(GetThumbnailScale() + ( MouseEvent.GetWheelDelta() * 0.05f ), 0.0f, 1.0f);		if ( DesiredScale != GetThumbnailScale() )		{			SetThumbnailScale( DesiredScale );		}				return FReply::Handled();	}	return FReply::Unhandled();}#if ECB_WIP_BREADCRUMBvoid SExtAssetView::OnFocusChanging( const FWeakWidgetPath& PreviousFocusPath, const FWidgetPath& NewWidgetPath, const FFocusEvent& InFocusEvent){	ResetQuickJump();}#endifTSharedRef<SExtAssetTileView> SExtAssetView::CreateTileView(){	return SNew(SExtAssetTileView)		.SelectionMode(SelectionMode)		.ListItemsSource(&FilteredAssetItems)		.OnGenerateTile(this, &SExtAssetView::MakeTileViewWidget)		.OnItemScrolledIntoView(this, &SExtAssetView::ItemScrolledIntoView) 		.OnContextMenuOpening(this, &SExtAssetView::OnGetContextMenuContent) 		.OnMouseButtonDoubleClick(this, &SExtAssetView::OnListMouseButtonDoubleClick) 		.OnSelectionChanged(this, &SExtAssetView::AssetSelectionChanged) 		.ItemHeight(this, &SExtAssetView::GetTileViewItemHeight) 		.ItemWidth(this, &SExtAssetView::GetTileViewItemWidth);}TSharedRef<SExtAssetListView> SExtAssetView::CreateListView(){	return SNew(SExtAssetListView)		.SelectionMode( SelectionMode )		.ListItemsSource(&FilteredAssetItems)		.OnGenerateRow(this, &SExtAssetView::MakeListViewWidget)		.OnItemScrolledIntoView(this, &SExtAssetView::ItemScrolledIntoView)		.OnContextMenuOpening(this, &SExtAssetView::OnGetContextMenuContent)		.OnMouseButtonDoubleClick(this, &SExtAssetView::OnListMouseButtonDoubleClick)		.OnSelectionChanged(this, &SExtAssetView::AssetSelectionChanged)		.ItemHeight(this, &SExtAssetView::GetListViewItemHeight);}TSharedRef<SExtAssetColumnView> SExtAssetView::CreateColumnView(){	TSharedPtr<SExtAssetColumnView> NewColumnView = SNew(SExtAssetColumnView)		.SelectionMode( SelectionMode )		.ListItemsSource(&FilteredAssetItems)		.OnGenerateRow(this, &SExtAssetView::MakeColumnViewWidget)		.OnItemScrolledIntoView(this, &SExtAssetView::ItemScrolledIntoView)		.OnContextMenuOpening(this, &SExtAssetView::OnGetContextMenuContent)		.OnMouseButtonDoubleClick(this, &SExtAssetView::OnListMouseButtonDoubleClick)		.OnSelectionChanged(this, &SExtAssetView::AssetSelectionChanged)		.Visibility(this, &SExtAssetView::GetColumnViewVisibility)		.HeaderRow		(			SNew(SHeaderRow)			.ResizeMode(ESplitterResizeMode::FixedSize)			+ SHeaderRow::Column(SortManager.NameColumnId)			.FillWidth(300)			.SortMode( TAttribute< EColumnSortMode::Type >::Create( TAttribute< EColumnSortMode::Type >::FGetter::CreateSP( this, &SExtAssetView::GetColumnSortMode, SortManager.NameColumnId ) ) )			.SortPriority(TAttribute< EColumnSortPriority::Type >::Create(TAttribute< EColumnSortPriority::Type >::FGetter::CreateSP(this, &SExtAssetView::GetColumnSortPriority, SortManager.NameColumnId)))			.OnSort( FOnSortModeChanged::CreateSP( this, &SExtAssetView::OnSortColumnHeader ) )			.DefaultLabel( LOCTEXT("Column_Name", "Name") )			.ShouldGenerateWidget(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateSP(this, &SExtAssetView::ShouldColumnGenerateWidget, SortManager.NameColumnId.ToString())))			.MenuContent()			[				CreateRowHeaderMenuContent(SortManager.NameColumnId.ToString())			]		);	NewColumnView->GetHeaderRow()->SetOnGetMaxRowSizeForColumn(FOnGetMaxRowSizeForColumn::CreateRaw(NewColumnView.Get(), &SExtAssetColumnView::GetMaxRowSizeForColumn));	NumVisibleColumns = HiddenColumnNames.Contains(SortManager.NameColumnId.ToString()) ? 0 : 1;	if(bShowTypeInColumnView)	{		NewColumnView->GetHeaderRow()->AddColumn(				SHeaderRow::Column(SortManager.ClassColumnId)				.FillWidth(160)				.SortMode(TAttribute< EColumnSortMode::Type >::Create(TAttribute< EColumnSortMode::Type >::FGetter::CreateSP(this, &SExtAssetView::GetColumnSortMode, SortManager.ClassColumnId)))				.SortPriority(TAttribute< EColumnSortPriority::Type >::Create(TAttribute< EColumnSortPriority::Type >::FGetter::CreateSP(this, &SExtAssetView::GetColumnSortPriority, SortManager.ClassColumnId)))				.OnSort(FOnSortModeChanged::CreateSP(this, &SExtAssetView::OnSortColumnHeader))				.DefaultLabel(LOCTEXT("Column_Class", "Type"))				.ShouldGenerateWidget(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateSP(this, &SExtAssetView::ShouldColumnGenerateWidget, SortManager.ClassColumnId.ToString())))				.MenuContent()				[					CreateRowHeaderMenuContent(SortManager.ClassColumnId.ToString())				]			);		NumVisibleColumns += HiddenColumnNames.Contains(SortManager.ClassColumnId.ToString()) ? 0 : 1;	}	if (bShowPathInColumnView)	{		NewColumnView->GetHeaderRow()->AddColumn(				SHeaderRow::Column(SortManager.PathColumnId)				.FillWidth(160)				.SortMode(TAttribute< EColumnSortMode::Type >::Create(TAttribute< EColumnSortMode::Type >::FGetter::CreateSP(this, &SExtAssetView::GetColumnSortMode, SortManager.PathColumnId)))				.SortPriority(TAttribute< EColumnSortPriority::Type >::Create(TAttribute< EColumnSortPriority::Type >::FGetter::CreateSP(this, &SExtAssetView::GetColumnSortPriority, SortManager.PathColumnId)))				.OnSort(FOnSortModeChanged::CreateSP(this, &SExtAssetView::OnSortColumnHeader))				.DefaultLabel(LOCTEXT("Column_Path", "Path"))				.ShouldGenerateWidget(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateSP(this, &SExtAssetView::ShouldColumnGenerateWidget, SortManager.PathColumnId.ToString())))				.MenuContent()				[					CreateRowHeaderMenuContent(SortManager.PathColumnId.ToString())				]			);		NumVisibleColumns += HiddenColumnNames.Contains(SortManager.PathColumnId.ToString()) ? 0 : 1;	}	return NewColumnView.ToSharedRef();}#if ECB_LEGACYbool SExtAssetView::IsValidSearchToken(const FString& Token) const{	if ( Token.Len() == 0 )	{		return false;	}	// A token may not be only apostrophe only, or it will match every asset because the text filter compares against the pattern Class'ObjectPath'	if ( Token.Len() == 1 && Token[0] == '\'' )	{		return false;	}	return true;}#endifvoid SExtAssetView::RefreshSourceItems(){	RecentlyLoadedOrChangedAssets.Reset();	RecentlyAddedAssets.Reset();	FilteredRecentlyAddedAssets.Reset();	QueriedAssetItems.Reset();	AssetItems.Reset();	FilteredAssetItems.Reset();	VisibleItems.Reset();	RelevantThumbnails.Reset();	Folders.Reset();	TArray<FExtAssetData>& Items = OnShouldFilterAsset.IsBound() ? QueriedAssetItems : AssetItems;	if (OnShouldFilterAsset.IsBound())	{		ECB_LOG(Display, TEXT("OnShouldFilterAsset.IsBound()"));	}#if ECB_TODO // show all?	const bool bShowAll = SourcesData.IsEmpty() && BackendFilter.IsEmpty();	if ( bShowAll )	{		// Include assets in memory		TSet<FName> PackageNamesToSkip = AssetRegistryModule.Get().GetCachedEmptyPackages();		for (FObjectIterator ObjIt; ObjIt; ++ObjIt)		{			if (ObjIt->IsAsset())			{				if (!InitialAssetFilter.PassesPackagePathFilter(ObjIt->GetOutermost()->GetFName()))				{					continue;				}				int32 Index = Items.Emplace(*ObjIt);				const FExtAssetData& AssetData = Items[Index];				if (!InitialAssetFilter.PassesRedirectorMainAssetFilter(AssetData))				{					Items.RemoveAtSwap(Index, 1, false);					continue;				}				PackageNamesToSkip.Add(AssetData.PackageName);			}		}		// Include assets on disk		const TMap<FName, const FAssetData*>& AssetDataMap = AssetRegistryModule.Get().GetAssetRegistryState()->GetObjectPathToAssetDataMap();		for (const TPair<FName, const FAssetData*>& AssetDataPair : AssetDataMap)		{			const FAssetData* AssetData = AssetDataPair.Value;			if (AssetData == nullptr)			{				continue;			}			// Make sure the asset's package was not loaded then the object was deleted/renamed			if (PackageNamesToSkip.Contains(AssetData->PackageName))			{				continue;			}			if (!InitialAssetFilter.PassesFilter(*AssetData))			{				continue;			}			Items.Emplace(*AssetData);		}		bShowClasses = IsShowingCppContent();		bWereItemsRecursivelyFiltered = true;	}	else#endif	{		// Assemble the filter using the current sources		// force recursion when the user is searching		const bool bRecurse = ShouldFilterRecursively();		const bool bUsingFolders = IsShowingFolders();		const bool bIsDynamicCollection = SourcesData.IsDynamicCollection();		FARFilter Filter = SourcesData.MakeFilter(bRecurse, bUsingFolders);		// Add the backend filters from the filter list		Filter.Append(BackendFilter);		bWereItemsRecursivelyFiltered = bRecurse;#if ECB_WIP_COLLECTION		if ( SourcesData.HasCollections() && Filter.ObjectPaths.Num() == 0 && !bIsDynamicCollection )		{			// This is an empty collection, no asset will pass the check		}		else#endif		{			#if ECB_FEA_ASYNC_ASSET_DISCOVERY					FExtContentBrowserSingleton::GetAssetRegistry().CacheAssetsAsync(Filter);#endif			// Add assets found in the asset registry			FExtContentBrowserSingleton::GetAssetRegistry().GetAssets(Filter, Items);		}#if ECB_WIP_INITIAL_ASSET		for (int32 AssetIdx = Items.Num() - 1; AssetIdx >= 0; --AssetIdx)		{			if (!InitialAssetFilter.PassesFilter(Items[AssetIdx]))			{				Items.RemoveAtSwap(AssetIdx);			}		}	}#endif	}}bool SExtAssetView::IsFiltering() const{	// In some cases we want to not filter recursively even if we have a backend filter (e.g. the open level window)	// Most of the time, bFilterRecursivelyWithBackendFilter is true	if (bFilterRecursivelyWithBackendFilter && !BackendFilter.IsEmpty())	{		return true;	}	// Otherwise, check if there are any non-inverse frontend filters selected	if (FrontendFilters.IsValid())	{		for (int32 FilterIndex = 0; FilterIndex < FrontendFilters->Num(); ++FilterIndex)		{			const auto* Filter = static_cast<FExtFrontendFilter*>(FrontendFilters->GetFilterAtIndex(FilterIndex).Get());			if (Filter)			{				if (!Filter->IsInverseFilter())				{					return true;				}			}		}	}	return false;}bool SExtAssetView::ShouldFilterRecursively() const{	const bool bSearchAndFilterRecursively = IsSearchAndFilterRecursively();	// Quick check for conditions which force recursive filtering	if (bUserSearching && bSearchAndFilterRecursively)	{		return true;	}	if (IsFiltering() && bSearchAndFilterRecursively)	{		return true;	}#if ECB_LEGACY	// In some cases we want to not filter recursively even if we have a backend filter (e.g. the open level window)	// Most of the time, bFilterRecursivelyWithBackendFilter is true	if ( bFilterRecursivelyWithBackendFilter && !BackendFilter.IsEmpty() && bSearchAndFilterRecursively)	{		return true;	}	// Otherwise, check if there are any non-inverse frontend filters selected	if (FrontendFilters.IsValid() && bSearchAndFilterRecursively)	{		for (int32 FilterIndex = 0; FilterIndex < FrontendFilters->Num(); ++FilterIndex)		{			const auto* Filter = static_cast<FExtFrontendFilter*>(FrontendFilters->GetFilterAtIndex(FilterIndex).Get());			if (Filter)			{				if (!Filter->IsInverseFilter())				{					return true;				}			}		}	}#endif	// No filters, do not override folder view with recursive filtering	return false;}void SExtAssetView::RefreshFilteredItems(){	//Build up a map of the existing AssetItems so we can preserve them while filtering	TMap< FName, TSharedPtr< FExtAssetViewAsset > > ItemToObjectPath;	for (int Index = 0; Index < FilteredAssetItems.Num(); Index++)	{		if(FilteredAssetItems[Index].IsValid() && FilteredAssetItems[Index]->GetType() != EExtAssetItemType::Folder)		{			TSharedPtr<FExtAssetViewAsset> Item = StaticCastSharedPtr<FExtAssetViewAsset>(FilteredAssetItems[Index]);			// Clear custom column data			Item->CustomColumnData.Reset();			Item->CustomColumnDisplayText.Reset();			ItemToObjectPath.Add( Item->Data.ObjectPath, Item );		}	}	// Empty all the filtered lists	FilteredAssetItems.Reset();	VisibleItems.Reset();	RelevantThumbnails.Reset();	Folders.Reset();	// true if the results from the asset registry query are filtered further by the content browser	const bool bIsFrontendFilterActive = IsFrontendFilterActive();	// true if we are looking at columns so we need to determine the majority asset type	const bool bGatherAssetTypeCount = CurrentViewType == EAssetViewType::Column;	TMap<FName, int32> AssetTypeCount;	if ( bIsFrontendFilterActive && FrontendFilters.IsValid() )	{		const bool bRecurse = ShouldFilterRecursively();#if ECB_WIP_SEARCH_RECURSE_TOGGLE		const bool bShouldHideFolders = (bUserSearching || IsFiltering()) && !IsSearchAndFilterRecursively();#else		const bool bShouldHideFolders = false;#endif		const bool bUsingFolders = IsShowingFolders() && !bShouldHideFolders;		FARFilter CombinedFilter = SourcesData.MakeFilter(bRecurse, bUsingFolders);		CombinedFilter.Append(BackendFilter);		// Let the frontend filters know the currently used filter in case it is necessary to conditionally filter based on path or class filters		for ( int32 FilterIdx = 0; FilterIdx < FrontendFilters->Num(); ++FilterIdx )		{			// There are only FFrontendFilters in this collection			const TSharedPtr<FExtFrontendFilter>& Filter = StaticCastSharedPtr<FExtFrontendFilter>( FrontendFilters->GetFilterAtIndex(FilterIdx) );			if ( Filter.IsValid() )			{				Filter->SetCurrentFilter(CombinedFilter);			}		}	}	if ( bIsFrontendFilterActive && bGatherAssetTypeCount )	{		// Check the frontend filter for every asset and keep track of how many assets were found of each type		for (int32 AssetIdx = 0; AssetIdx < AssetItems.Num(); ++AssetIdx)		{			const FExtAssetData& AssetData = AssetItems[AssetIdx];			if ( PassesCurrentFrontendFilter(AssetData) )			{				const TSharedPtr< FExtAssetViewAsset >* AssetItem = ItemToObjectPath.Find( AssetData.ObjectPath );				if ( AssetItem != NULL )				{					FilteredAssetItems.Add(*AssetItem);				}				else				{					FilteredAssetItems.Add(MakeShareable(new FExtAssetViewAsset(AssetData)));				}				int32* TypeCount = AssetTypeCount.Find(AssetData.AssetClass);				if ( TypeCount )				{					(*TypeCount)++;				}				else				{					AssetTypeCount.Add(AssetData.AssetClass, 1);				}			}		}	}	else if ( bIsFrontendFilterActive && !bGatherAssetTypeCount )	{		// Check the frontend filter for every asset and don't worry about asset type counts		for (int32 AssetIdx = 0; AssetIdx < AssetItems.Num(); ++AssetIdx)		{			const FExtAssetData& AssetData = AssetItems[AssetIdx];			if ( PassesCurrentFrontendFilter(AssetData) )			{				const TSharedPtr< FExtAssetViewAsset >* AssetItem = ItemToObjectPath.Find( AssetData.ObjectPath );				if ( AssetItem != NULL )				{					FilteredAssetItems.Add(*AssetItem);				}				else				{					FilteredAssetItems.Add(MakeShareable(new FExtAssetViewAsset(AssetData)));				}			}		}	}	else if ( !bIsFrontendFilterActive && bGatherAssetTypeCount )	{		// Don't need to check the frontend filter for every asset but keep track of how many assets were found of each type		for (int32 AssetIdx = 0; AssetIdx < AssetItems.Num(); ++AssetIdx)		{			const FExtAssetData& AssetData = AssetItems[AssetIdx];			const TSharedPtr< FExtAssetViewAsset >* AssetItem = ItemToObjectPath.Find( AssetData.ObjectPath );			if ( AssetItem != NULL )			{				FilteredAssetItems.Add(*AssetItem);			}			else			{				FilteredAssetItems.Add(MakeShareable(new FExtAssetViewAsset(AssetData)));			}			int32* TypeCount = AssetTypeCount.Find(AssetData.AssetClass);			if ( TypeCount )			{				(*TypeCount)++;			}			else			{				AssetTypeCount.Add(AssetData.AssetClass, 1);			}		}	}	else if ( !bIsFrontendFilterActive && !bGatherAssetTypeCount )	{		// Don't check the frontend filter and don't count the number of assets of each type. Just add all assets.		for (int32 AssetIdx = 0; AssetIdx < AssetItems.Num(); ++AssetIdx)		{			const FExtAssetData& AssetData = AssetItems[AssetIdx];			const TSharedPtr< FExtAssetViewAsset >* AssetItem = ItemToObjectPath.Find( AssetData.ObjectPath );			if ( AssetItem != NULL )			{				FilteredAssetItems.Add(*AssetItem);			}			else			{				FilteredAssetItems.Add(MakeShareable(new FExtAssetViewAsset(AssetData)));			}		}	}	else	{		// The above cases should handle all combinations of bIsFrontendFilterActive and bGatherAssetTypeCount		ensure(0);	}	if ( bGatherAssetTypeCount )	{		int32 HighestCount = 0;		FName HighestType;		for ( auto TypeIt = AssetTypeCount.CreateConstIterator(); TypeIt; ++TypeIt )		{			if ( TypeIt.Value() > HighestCount )			{				HighestType = TypeIt.Key();				HighestCount = TypeIt.Value();			}		}		SetMajorityAssetType(HighestType);	}}void SExtAssetView::RefreshFolders(){	if(!IsShowingFolders() || ShouldFilterRecursively())	{		return;	}#if ECB_WIP_SEARCH_RECURSE_TOGGLE	const bool bShouldHideFolders = (bUserSearching || IsFiltering()) && !IsSearchAndFilterRecursively();	if (bShouldHideFolders)	{		return;	}#endif		// Split the selected paths into asset and class paths	TArray<FName> AssetPathsToShow;	TArray<FName> ClassPathsToShow;	for(const FName& PackagePath : SourcesData.PackagePaths)	{		if(ExtContentBrowserUtils::IsClassPath(PackagePath.ToString()))		{			ClassPathsToShow.Add(PackagePath);		}		else		{			AssetPathsToShow.Add(PackagePath);		}	}	TArray<FString> FoldersToAdd;	const bool bDisplayEmpty = IsShowingEmptyFolders();	{		TSet<FName> SubPaths;		for(const FName& PackagePath : AssetPathsToShow)		{			SubPaths.Reset();			FExtContentBrowserSingleton::GetAssetRegistry().GetOrCacheSubPaths(PackagePath, SubPaths, false);			for(const FName& SubPath : SubPaths)			{				FString SubPathString = SubPath.ToString();				if (!bDisplayEmpty && FExtContentBrowserSingleton::GetAssetRegistry().IsEmptyFolder(SubPathString))				{					continue;				}								if(!Folders.Contains(SubPathString))				{					FoldersToAdd.Add(SubPathString);				}			}		}	}	// Add folders for any child collections of the currently selected collections	if(SourcesData.HasCollections())	{		FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();				TArray<FCollectionNameType> ChildCollections;		for(const FCollectionNameType& Collection : SourcesData.Collections)		{			ChildCollections.Reset();			CollectionManagerModule.Get().GetChildCollections(Collection.Name, Collection.Type, ChildCollections);			for(const FCollectionNameType& ChildCollection : ChildCollections)			{				// Use "Collections" as the root of the path to avoid this being confused with other asset view folders - see ContentBrowserUtils::IsCollectionPath				FoldersToAdd.Add(FString::Printf(TEXT("/Collections/%s/%s"), ECollectionShareType::ToString(ChildCollection.Type), *ChildCollection.Name.ToString()));			}		}	}	if(FoldersToAdd.Num() > 0)	{		for(const FString& FolderPath : FoldersToAdd)		{			FilteredAssetItems.Add(MakeShareable(new FExtAssetViewFolder(FolderPath)));			Folders.Add(FolderPath);		}		RefreshList();		bPendingSortFilteredItems = true;	}}void SExtAssetView::SetMajorityAssetType(FName NewMajorityAssetType){	auto IsFixedColumn = [this](FName InColumnId)	{		const bool bIsFixedNameColumn = InColumnId == SortManager.NameColumnId;		const bool bIsFixedClassColumn = bShowTypeInColumnView && InColumnId == SortManager.ClassColumnId;		const bool bIsFixedPathColumn = bShowPathInColumnView && InColumnId == SortManager.PathColumnId;		return bIsFixedNameColumn || bIsFixedClassColumn || bIsFixedPathColumn;	};	if ( NewMajorityAssetType != MajorityAssetType )	{		ECB_LOG(Display , TEXT("The majority of assets in the view are of type: %s"), *NewMajorityAssetType.ToString());		MajorityAssetType = NewMajorityAssetType;		TArray<FName> AddedColumns;		// Since the asset type has changed, remove all columns except name and class		const TIndirectArray<SHeaderRow::FColumn>& Columns = ColumnView->GetHeaderRow()->GetColumns();		for ( int32 ColumnIdx = Columns.Num() - 1; ColumnIdx >= 0; --ColumnIdx )		{			const FName ColumnId = Columns[ColumnIdx].ColumnId;			if ( ColumnId != NAME_None && !IsFixedColumn(ColumnId) )			{				ColumnView->GetHeaderRow()->RemoveColumn(ColumnId);			}		}		// Keep track of the current column name to see if we need to change it now that columns are being removed		// Name, Class, and Path are always relevant		struct FSortOrder		{			bool bSortRelevant;			FName SortColumn;			FSortOrder(bool bInSortRelevant, const FName& InSortColumn) : bSortRelevant(bInSortRelevant), SortColumn(InSortColumn) {}		};		TArray<FSortOrder> CurrentSortOrder;		for (int32 PriorityIdx = 0; PriorityIdx < EColumnSortPriority::Max; PriorityIdx++)		{			const FName SortColumn = SortManager.GetSortColumnId(static_cast<EColumnSortPriority::Type>(PriorityIdx));			if (SortColumn != NAME_None)			{				const bool bSortRelevant = SortColumn == FAssetViewSortManager::NameColumnId					|| SortColumn == FAssetViewSortManager::ClassColumnId					|| SortColumn == FAssetViewSortManager::PathColumnId;				CurrentSortOrder.Add(FSortOrder(bSortRelevant, SortColumn));			}		}		// Add custom columns		for (const FAssetViewCustomColumn& Column : CustomColumns)		{			FName TagName = Column.ColumnName;			if (AddedColumns.Contains(TagName))			{				continue;			}			AddedColumns.Add(TagName);			ColumnView->GetHeaderRow()->AddColumn(				SHeaderRow::Column(TagName)				.SortMode(TAttribute< EColumnSortMode::Type >::Create(TAttribute< EColumnSortMode::Type >::FGetter::CreateSP(this, &SExtAssetView::GetColumnSortMode, TagName)))				.SortPriority(TAttribute< EColumnSortPriority::Type >::Create(TAttribute< EColumnSortPriority::Type >::FGetter::CreateSP(this, &SExtAssetView::GetColumnSortPriority, TagName)))				.OnSort(FOnSortModeChanged::CreateSP(this, &SExtAssetView::OnSortColumnHeader))				.DefaultLabel(Column.DisplayName)				.DefaultTooltip(Column.TooltipText)				.FillWidth(180)				.ShouldGenerateWidget(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateSP(this, &SExtAssetView::ShouldColumnGenerateWidget, TagName.ToString())))				.MenuContent()				[					CreateRowHeaderMenuContent(TagName.ToString())				]);			NumVisibleColumns += HiddenColumnNames.Contains(TagName.ToString()) ? 0 : 1;			// If we found a tag the matches the column we are currently sorting on, there will be no need to change the column			for (int32 SortIdx = 0; SortIdx < CurrentSortOrder.Num(); SortIdx++)			{				if (TagName == CurrentSortOrder[SortIdx].SortColumn)				{					CurrentSortOrder[SortIdx].bSortRelevant = true;				}			}		}		// If we have a new majority type, add the new type's columns		if ( NewMajorityAssetType != NAME_None )		{			// Determine the columns by querying the CDO for the tag map			UClass* TypeClass = FindObject<UClass>(nullptr, *NewMajorityAssetType.ToString());			if ( TypeClass )			{				UObject* CDO = TypeClass->GetDefaultObject();				if ( CDO )				{					TArray<UObject::FAssetRegistryTag> AssetRegistryTags;					CDO->GetAssetRegistryTags(AssetRegistryTags);					// Add a column for every tag that isn't hidden or using a reserved name					for ( auto TagIt = AssetRegistryTags.CreateConstIterator(); TagIt; ++TagIt )					{						if ( TagIt->Type != UObject::FAssetRegistryTag::TT_Hidden )						{							const FName TagName = TagIt->Name;							if (IsFixedColumn(TagName))							{								// Reserved name								continue;							}							//if ( !OnAssetTagWantsToBeDisplayed.IsBound() || OnAssetTagWantsToBeDisplayed.Execute(NewMajorityAssetType, TagName) )							{								if (AddedColumns.Contains(TagName))								{									continue;								}								AddedColumns.Add(TagName);								// Get tag metadata								TMap<FName, UObject::FAssetRegistryTagMetadata> MetadataMap;								CDO->GetAssetRegistryTagMetadata(MetadataMap);								const UObject::FAssetRegistryTagMetadata* Metadata = MetadataMap.Find(TagName);								FText DisplayName;								if (Metadata != nullptr && !Metadata->DisplayName.IsEmpty())								{									DisplayName = Metadata->DisplayName;								}								else								{									DisplayName = FText::FromName(TagName);								}								FText TooltipText;								if (Metadata != nullptr && !Metadata->TooltipText.IsEmpty())								{									TooltipText = Metadata->TooltipText;								}								else								{									// If the tag name corresponds to a property name, use the property tooltip									FProperty* Property = FindFProperty<FProperty>(TypeClass, TagName);									TooltipText = (Property != nullptr) ? Property->GetToolTipText() : FText::FromString(FName::NameToDisplayString(TagName.ToString(), false));								}								ColumnView->GetHeaderRow()->AddColumn(									SHeaderRow::Column(TagName)									.SortMode(TAttribute< EColumnSortMode::Type >::Create(TAttribute< EColumnSortMode::Type >::FGetter::CreateSP(this, &SExtAssetView::GetColumnSortMode, TagName)))									.SortPriority(TAttribute< EColumnSortPriority::Type >::Create(TAttribute< EColumnSortPriority::Type >::FGetter::CreateSP(this, &SExtAssetView::GetColumnSortPriority, TagName)))									.OnSort(FOnSortModeChanged::CreateSP(this, &SExtAssetView::OnSortColumnHeader))									.DefaultLabel(DisplayName)									.DefaultTooltip(TooltipText)									.FillWidth(180)									.ShouldGenerateWidget(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateSP(this, &SExtAssetView::ShouldColumnGenerateWidget, TagName.ToString())))									.MenuContent()									[										CreateRowHeaderMenuContent(TagName.ToString())									]);																								NumVisibleColumns += HiddenColumnNames.Contains(TagName.ToString()) ? 0 : 1;								// If we found a tag the matches the column we are currently sorting on, there will be no need to change the column								for (int32 SortIdx = 0; SortIdx < CurrentSortOrder.Num(); SortIdx++)								{									if (TagName == CurrentSortOrder[SortIdx].SortColumn)									{										CurrentSortOrder[SortIdx].bSortRelevant = true;									}								}							}						}					}				}						}			}		// Are any of the sort columns irrelevant now, if so remove them from the list		bool CurrentSortChanged = false;		for (int32 SortIdx = CurrentSortOrder.Num() - 1; SortIdx >= 0; SortIdx--)		{			if (!CurrentSortOrder[SortIdx].bSortRelevant)			{				CurrentSortOrder.RemoveAt(SortIdx);				CurrentSortChanged = true;			}		}		if (CurrentSortOrder.Num() > 0 && CurrentSortChanged)		{			// Sort order has changed, update the columns keeping those that are relevant			int32 PriorityNum = EColumnSortPriority::Primary;			for (int32 SortIdx = 0; SortIdx < CurrentSortOrder.Num(); SortIdx++)			{				check(CurrentSortOrder[SortIdx].bSortRelevant);				if (!SortManager.SetOrToggleSortColumn(static_cast<EColumnSortPriority::Type>(PriorityNum), CurrentSortOrder[SortIdx].SortColumn))				{					// Toggle twice so mode is preserved if this isn't a new column assignation					SortManager.SetOrToggleSortColumn(static_cast<EColumnSortPriority::Type>(PriorityNum), CurrentSortOrder[SortIdx].SortColumn);				}								bPendingSortFilteredItems = true;				PriorityNum++;			}		}		else if (CurrentSortOrder.Num() == 0)		{			// If the current sort column is no longer relevant, revert to "Name" and resort when convenient			SortManager.ResetSort();			bPendingSortFilteredItems = true;		}	}}#if ECB_WIP_COLLECTIONvoid SExtAssetView::OnAssetsAddedToCollection( const FCollectionNameType& Collection, const TArray< FName >& ObjectPaths ){	if ( !SourcesData.Collections.Contains( Collection ) )	{		return;	}	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));	for (int Index = 0; Index < ObjectPaths.Num(); Index++)	{		OnAssetAdded( AssetRegistryModule.Get().GetAssetByObjectPath( ObjectPaths[Index] ) );	}}void SExtAssetView::OnAssetsRemovedFromCollection(const FCollectionNameType& Collection, const TArray< FName >& ObjectPaths){	if (!SourcesData.Collections.Contains(Collection))	{		return;	}	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));	for (int Index = 0; Index < ObjectPaths.Num(); Index++)	{		OnAssetRemoved(AssetRegistryModule.Get().GetAssetByObjectPath(ObjectPaths[Index]));	}}#endif#if ECB_FEA_ASYNC_ASSET_DISCOVERYvoid SExtAssetView::ProcessRecentlyAddedAssets(){	if (		(RecentlyAddedAssets.Num() > 2048) ||		(RecentlyAddedAssets.Num() > 0 && FPlatformTime::Seconds() - LastProcessAddsTime >= TimeBetweenAddingNewAssets)		)	{		RunAssetsThroughBackendFilter(RecentlyAddedAssets);		FilteredRecentlyAddedAssets.Append(RecentlyAddedAssets);		RecentlyAddedAssets.Reset();		LastProcessAddsTime = FPlatformTime::Seconds();	}	if (FilteredRecentlyAddedAssets.Num() > 0)	{		double TickStartTime = FPlatformTime::Seconds();		bool bNeedsRefresh = false;		TSet<FName> ExistingObjectPaths;		for ( auto AssetIt = AssetItems.CreateConstIterator(); AssetIt; ++AssetIt )		{			ExistingObjectPaths.Add((*AssetIt).ObjectPath);		}		for ( auto AssetIt = QueriedAssetItems.CreateConstIterator(); AssetIt; ++AssetIt )		{			ExistingObjectPaths.Add((*AssetIt).ObjectPath);		}		int32 AssetIdx = 0;		for ( ; AssetIdx < FilteredRecentlyAddedAssets.Num(); ++AssetIdx )		{			const FExtAssetData& AssetData = FilteredRecentlyAddedAssets[AssetIdx];			if ( !ExistingObjectPaths.Contains(AssetData.ObjectPath) )			{				if ( AssetData.AssetClass != UObjectRedirector::StaticClass()->GetFName() || AssetData.IsUAsset() )				{					if ( !OnShouldFilterAsset.IsBound() || !OnShouldFilterAsset.Execute(AssetData) )					{						// Add the asset to the list						int32 AddedAssetIdx = AssetItems.Add(AssetData);						ExistingObjectPaths.Add(AssetData.ObjectPath);						if (!IsFrontendFilterActive() || PassesCurrentFrontendFilter(AssetData))						{							FilteredAssetItems.Add(MakeShareable(new FExtAssetViewAsset(AssetData)));							bNeedsRefresh = true;							bPendingSortFilteredItems = true;						}					}				}			}			if ( (FPlatformTime::Seconds() - TickStartTime) > MaxSecondsPerFrame)			{				// Increment the index to properly trim the buffer below				++AssetIdx;				break;			}		}		// Trim the results array		if (AssetIdx > 0)		{			FilteredRecentlyAddedAssets.RemoveAt(0, AssetIdx);		}		if (bNeedsRefresh)		{			RefreshList();		}	}}#endif#if ECB_LEGACYvoid SExtAssetView::OnAssetAdded(const FExtAssetData& AssetData){	RecentlyAddedAssets.Add(AssetData);}void SExtAssetView::OnAssetRemoved(const FAssetData& AssetData){	RemoveAssetByPath( AssetData.ObjectPath );	RecentlyAddedAssets.RemoveSingleSwap(AssetData);}void SExtAssetView::OnAssetRegistryPathAdded(const FString& Path){	if(IsShowingFolders() && !ShouldFilterRecursively())	{		TSharedRef<FEmptyFolderVisibilityManager> EmptyFolderVisibilityManager = FContentBrowserSingleton::Get().GetEmptyFolderVisibilityManager();		// If this isn't a developer folder or we want to show them, continue		const bool bDisplayEmpty = IsShowingEmptyFolders();		const bool bDisplayL10N = IsShowingLocalizedContent();		if ((bDisplayEmpty || EmptyFolderVisibilityManager->ShouldShowPath(Path)) &&  			(bDisplayL10N || !ContentBrowserUtils::IsLocalizationFolder(Path))			)		{			for (const FName& SourcePathName : SourcesData.PackagePaths)			{				// Ensure that /Folder2 is not considered a subfolder of /Folder by appending /				FString SourcePath = SourcePathName.ToString() / TEXT("");				if(Path.StartsWith(SourcePath))				{					const FString SubPath = Path.RightChop(SourcePath.Len());					TArray<FString> SubPathItemList;					SubPath.ParseIntoArray(SubPathItemList, TEXT("/"), /*InCullEmpty=*/true);					if (SubPathItemList.Num() > 0)					{						const FString NewSubFolder = SourcePath / SubPathItemList[0];						if (!Folders.Contains(NewSubFolder))						{							FilteredAssetItems.Add(MakeShareable(new FAssetViewFolder(NewSubFolder)));							RefreshList();							Folders.Add(NewSubFolder);							bPendingSortFilteredItems = true;						}					}				}			}		}	}}void SExtAssetView::OnAssetRegistryPathRemoved(const FString& Path){	FString* Folder = Folders.Find(Path);	if(Folder != NULL)	{		Folders.Remove(Path);		for (int32 AssetIdx = 0; AssetIdx < FilteredAssetItems.Num(); ++AssetIdx)		{			if(FilteredAssetItems[AssetIdx]->GetType() == EAssetItemType::Folder)			{				if ( StaticCastSharedPtr<FAssetViewFolder>(FilteredAssetItems[AssetIdx])->FolderPath == Path )				{					// Found the folder in the filtered items list, remove it					FilteredAssetItems.RemoveAt(AssetIdx);					RefreshList();					break;				}			}		}	}}void SExtAssetView::OnFolderPopulated(const FString& Path){	OnAssetRegistryPathAdded(Path);}void SExtAssetView::RemoveAssetByPath( const FName& ObjectPath ){	bool bFoundAsset = false;	for (int32 AssetIdx = 0; AssetIdx < AssetItems.Num(); ++AssetIdx)	{		if ( AssetItems[AssetIdx].ObjectPath == ObjectPath )		{			// Found the asset in the cached list, remove it			AssetItems.RemoveAt(AssetIdx);			bFoundAsset = true;			break;		}	}	if ( bFoundAsset )	{		// If it was in the AssetItems list, see if it is also in the FilteredAssetItems list		for (int32 AssetIdx = 0; AssetIdx < FilteredAssetItems.Num(); ++AssetIdx)		{			if(FilteredAssetItems[AssetIdx].IsValid() && FilteredAssetItems[AssetIdx]->GetType() != EAssetItemType::Folder)			{				if ( StaticCastSharedPtr<FExtAssetViewAsset>(FilteredAssetItems[AssetIdx])->Data.ObjectPath == ObjectPath && !FilteredAssetItems[AssetIdx]->IsTemporaryItem() )				{					// Found the asset in the filtered items list, remove it					FilteredAssetItems.RemoveAt(AssetIdx);					RefreshList();					break;				}			}		}	}	else	{		//Make sure we don't have the item still queued up for processing		for (int32 AssetIdx = 0; AssetIdx < QueriedAssetItems.Num(); ++AssetIdx)		{			if ( QueriedAssetItems[AssetIdx].ObjectPath == ObjectPath )			{				// Found the asset in the cached list, remove it				QueriedAssetItems.RemoveAt(AssetIdx);				bFoundAsset = true;				break;			}		}	}}void SExtAssetView::OnCollectionRenamed( const FCollectionNameType& OriginalCollection, const FCollectionNameType& NewCollection ){	int32 FoundIndex = INDEX_NONE;	if ( SourcesData.Collections.Find( OriginalCollection, FoundIndex ) )	{		SourcesData.Collections[ FoundIndex ] = NewCollection;	}}void SExtAssetView::OnCollectionUpdated( const FCollectionNameType& Collection ){	// A collection has changed in some way, so we need to refresh our backend list	RequestSlowFullListRefresh();}void SExtAssetView::OnAssetRenamed(const FAssetData& AssetData, const FString& OldObjectPath){	// Remove the old asset, if it exists	FName OldObjectPackageName = *OldObjectPath;	RemoveAssetByPath( OldObjectPackageName );	RecentlyAddedAssets.RemoveAllSwap( [&](const FAssetData& Other) { return Other.ObjectPath == OldObjectPackageName; } );	// Add the new asset, if it should be in the cached list	OnAssetAdded( AssetData );	// Force an update of the recently added asset next frame	RequestAddNewAssetsNextFrame();}void SExtAssetView::OnAssetLoaded(UObject* Asset){	if (Asset == nullptr)	{		return;	}	FName AssetPathName = FName(*Asset->GetPathName());	RecentlyLoadedOrChangedAssets.Add( FAssetData(Asset) );	UTexture2D* Texture2D = Cast<UTexture2D>(Asset);	UMaterial* Material = Texture2D ? nullptr : Cast<UMaterial>(Asset);	if ((Texture2D && !Texture2D->bForceMiplevelsToBeResident) || Material)	{		bool bHasWidgetForAsset = false;		switch (GetCurrentViewType())		{		case EAssetViewType::List:			bHasWidgetForAsset = ListView->HasWidgetForAsset(AssetPathName);			break;		case EAssetViewType::Tile:			bHasWidgetForAsset = TileView->HasWidgetForAsset(AssetPathName);			break;		default:			bHasWidgetForAsset = false;			break;		}		if (bHasWidgetForAsset)		{			if (Texture2D)			{				Texture2D->bForceMiplevelsToBeResident = true;			}			else if (Material)			{				Material->SetForceMipLevelsToBeResident(true, true, -1.0f);			}		}	};}void SExtAssetView::OnObjectPropertyChanged(UObject* Object, FPropertyChangedEvent& PropertyChangedEvent){	if (Object != nullptr && Object->IsAsset())	{		RecentlyLoadedOrChangedAssets.Add(FAssetData(Object));	}}void SExtAssetView::OnClassHierarchyUpdated(){	// The class hierarchy has changed in some way, so we need to refresh our backend list	RequestSlowFullListRefresh();}#endifvoid SExtAssetView::OnAssetUpdated(const FExtAssetData& AssetData){	RecentlyLoadedOrChangedAssets.Add(AssetData);}void SExtAssetView::OnFrontendFiltersChanged(){	RequestQuickFrontendListRefresh();	// If we're not operating on recursively filtered data, we need to ensure a full slow	// refresh is performed.	if ( ShouldFilterRecursively() && !bWereItemsRecursivelyFiltered )	{		RequestSlowFullListRefresh();	}}bool SExtAssetView::IsFrontendFilterActive() const{	return ( FrontendFilters.IsValid() && FrontendFilters->Num() > 0 );}bool SExtAssetView::PassesCurrentFrontendFilter(const FExtAssetData& Item) const{	// Check the frontend filters list	if ( FrontendFilters.IsValid() && !FrontendFilters->PassesAllFilters(Item) )	{		return false;	}	return true;}#if ECB_FEA_ASYNC_ASSET_DISCOVERYbool SExtAssetView::PassesCurrentBackendFilter(const FExtAssetData& Item) const{	TArray<FExtAssetData> AssetDataList;	AssetDataList.Add(Item);	RunAssetsThroughBackendFilter(AssetDataList);	return AssetDataList.Num() == 1;}void SExtAssetView::RunAssetsThroughBackendFilter(TArray<FExtAssetData>& InOutAssetDataList) const{	const bool bRecurse = ShouldFilterRecursively();	const bool bUsingFolders = IsShowingFolders();	const bool bIsDynamicCollection = SourcesData.IsDynamicCollection();	FARFilter Filter = SourcesData.MakeFilter(bRecurse, bUsingFolders);		if ( SourcesData.HasCollections() && Filter.SoftObjectPaths.Num() == 0 && !bIsDynamicCollection )	{		// This is an empty collection, no asset will pass the check		InOutAssetDataList.Reset();	}	else	{		// Actually append the backend filter		Filter.Append(BackendFilter);		FExtContentBrowserSingleton::GetAssetRegistry().RunAssetsThroughFilter(InOutAssetDataList, Filter);#if ECB_LEGACY		if ( SourcesData.HasCollections() && !bIsDynamicCollection )		{			// Include objects from child collections if we're recursing			const ECollectionRecursionFlags::Flags CollectionRecursionMode = (Filter.bRecursivePaths) ? ECollectionRecursionFlags::SelfAndChildren : ECollectionRecursionFlags::Self;			FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();			TArray< FName > CollectionObjectPaths;			for (const FCollectionNameType& Collection : SourcesData.Collections)			{				CollectionManagerModule.Get().GetObjectsInCollection(Collection.Name, Collection.Type, CollectionObjectPaths, CollectionRecursionMode);			}			for ( int32 AssetDataIdx = InOutAssetDataList.Num() - 1; AssetDataIdx >= 0; --AssetDataIdx )			{				const FAssetData& AssetData = InOutAssetDataList[AssetDataIdx];				if ( !CollectionObjectPaths.Contains( AssetData.ObjectPath ) )				{					InOutAssetDataList.RemoveAtSwap(AssetDataIdx);				}			}		}#endif	}}#endifvoid SExtAssetView::SortList(bool bSyncToSelection){	SortManager.SortList(FilteredAssetItems, MajorityAssetType, CustomColumns);	// Update the thumbnails we were using since the order has changed	bPendingUpdateThumbnails = true;	if (bSyncToSelection)	{		// Make sure the selection is in view		const bool bFocusOnSync = false;		SyncToSelection(bFocusOnSync);	}	RefreshList();	bPendingSortFilteredItems = false;	LastSortTime = CurrentTime;}FLinearColor SExtAssetView::GetThumbnailHintColorAndOpacity() const{	//We update this color in tick instead of here as an optimization	return ThumbnailHintColorAndOpacity;}FSlateColor SExtAssetView::GetViewButtonForegroundColor() const{	static const FName InvertedForegroundName("InvertedForeground");	static const FName DefaultForegroundName("DefaultForeground");	return ViewOptionsComboButton->IsHovered() ? FAppStyle::GetSlateColor(InvertedForegroundName) : FAppStyle::GetSlateColor(DefaultForegroundName);}TSharedRef<SWidget> SExtAssetView::GetViewButtonContent(){	UExtContentBrowserSettings* ExtContentBrowserSetting = GetMutableDefault<UExtContentBrowserSettings>();	// Get all menu extenders for this context menu from the content browser module	FExtContentBrowserModule& ExtContentBrowserModule = FModuleManager::GetModuleChecked<FExtContentBrowserModule>( TEXT("ExtContentBrowser") );	TArray<FExtContentBrowserModule::FExtContentBrowserMenuExtender> MenuExtenderDelegates = ExtContentBrowserModule.GetAllAssetViewViewMenuExtenders();	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, /*bCloseSelfOnly=*/ true);	MenuBuilder.BeginSection("AssetViewType", LOCTEXT("ViewTypeHeading", "View Type"));	{		MenuBuilder.AddMenuEntry(			LOCTEXT("TileViewOption", "Tiles"),			LOCTEXT("TileViewOptionToolTip", "View assets as tiles in a grid."),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP( this, &SExtAssetView::SetCurrentViewTypeFromMenu, EAssetViewType::Tile ),				FCanExecuteAction(),				FIsActionChecked::CreateSP( this, &SExtAssetView::IsCurrentViewType, EAssetViewType::Tile )				),			NAME_None,			EUserInterfaceActionType::RadioButton			);		MenuBuilder.AddMenuEntry(			LOCTEXT("ListViewOption", "List"),			LOCTEXT("ListViewOptionToolTip", "View assets in a list with thumbnails."),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP( this, &SExtAssetView::SetCurrentViewTypeFromMenu, EAssetViewType::List ),				FCanExecuteAction(),				FIsActionChecked::CreateSP( this, &SExtAssetView::IsCurrentViewType, EAssetViewType::List )				),			NAME_None,			EUserInterfaceActionType::RadioButton			);		MenuBuilder.AddMenuEntry(			LOCTEXT("ColumnViewOption", "Columns"),			LOCTEXT("ColumnViewOptionToolTip", "View assets in a list with columns of details."),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP( this, &SExtAssetView::SetCurrentViewTypeFromMenu, EAssetViewType::Column ),				FCanExecuteAction(),				FIsActionChecked::CreateSP( this, &SExtAssetView::IsCurrentViewType, EAssetViewType::Column )				),			NAME_None,			EUserInterfaceActionType::RadioButton			);	}	MenuBuilder.EndSection();	if (GetColumnViewVisibility() == EVisibility::Visible)	{		MenuBuilder.BeginSection("AssetColumns", LOCTEXT("ToggleColumnsHeading", "Columns"));		{			MenuBuilder.AddMenuEntry(				LOCTEXT("ShowAssetTypeColumns", "Show Asset Type Columns"),				LOCTEXT("ShowAssetTypeColumnsTooltip", "Show or hide major asset type columns."),				FSlateIcon(),				FUIAction(					FExecuteAction::CreateSP(this, &SExtAssetView::ToggleMajorAssetTypeColumns),					FCanExecuteAction(),					FIsActionChecked::CreateSP(this, &SExtAssetView::IsShowingMajorAssetTypeColumns)				),				NAME_None,				EUserInterfaceActionType::ToggleButton			);			MenuBuilder.AddSubMenu(				LOCTEXT("ToggleColumnsMenu", "Toggle Columns"),				LOCTEXT("ToggleColumnsMenuTooltip", "Show or hide specific columns."),				FNewMenuDelegate::CreateSP(this, &SExtAssetView::FillToggleColumnsMenu),				false,				FSlateIcon(),				false			);			MenuBuilder.AddMenuEntry(				LOCTEXT("ResetColumns", "Reset Columns"),				LOCTEXT("ResetColumnsToolTip", "Reset all columns to be visible again."),				FSlateIcon(),				FUIAction(FExecuteAction::CreateSP(this, &SExtAssetView::ResetColumns)),				NAME_None,				EUserInterfaceActionType::Button			);#if ECB_WIP_MORE_VIEWTYPE			MenuBuilder.AddMenuEntry(				LOCTEXT("ExportColumns", "Export to CSV"),				LOCTEXT("ExportColumnsToolTip", "Export column data to CSV."),				FSlateIcon(),				FUIAction(FExecuteAction::CreateSP(this, &SExtAssetView::ExportColumns)),				NAME_None,				EUserInterfaceActionType::Button			);#endif		}		MenuBuilder.EndSection();	}		MenuBuilder.BeginSection("View", LOCTEXT("ViewHeading", "View"));	{#if ECB_FEA_ENGINE_VERSION_OVERLAY		MenuBuilder.AddMenuEntry(			LOCTEXT("ShowEngineVersionOverlayOption", "Show Engine Version Overlay"),			LOCTEXT("ShowEngineVersionOverlayToolTip", "Show saved engine version on asset thumbnail?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleShowEngineVersionOverlayButton),				FCanExecuteAction(),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsShowingEngineVersionOverlayButton)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#endif#if ECB_FEA_VALIDATE_OVERLAY		MenuBuilder.AddMenuEntry(			LOCTEXT("ShowValidationOverlayOption", "Show Validation Status Overlay"),			LOCTEXT("ShowValidationOverlayToolTip", "Show if an uasset file's validation status as overlay on asset thumbnail?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleShowValidationStatusOverlayButton),				FCanExecuteAction(),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsShowingValidationStatusOverlayButton)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#endif#if ECB_WIP_CONTENT_TYPE_OVERLAY		MenuBuilder.AddMenuEntry(			LOCTEXT("ShowContentTypeOverlayOption", "Show Content Type Overlay"),			LOCTEXT("ShowContentTypeOverlayToolTip", "Show project/plugin/orphan content type as overlay"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleShowContentTypeOverlayButton),				FCanExecuteAction(),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsShowingContentTypeOverlayButton)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#endif#if ECB_FEA_SHOW_INVALID		MenuBuilder.AddMenuEntry(			LOCTEXT("ShowInvalidAssets", "Show Invalid Assets"),			LOCTEXT("ShowInvalidAssetsToolTip", "Display invalid assets in the asset view?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleShowInvalidAssets),				FCanExecuteAction(),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsShowingInvalidAssets)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#endif#if ECB_WIP_TOGGLE_TOOLBAR_BUTTON		MenuBuilder.AddMenuEntry(			LOCTEXT("ShowToolbarButtonTooltipOption", "Show UAsset Toolbar Button"),			LOCTEXT("ShowToolbarButtonOptionToolTip", "Show Open UAsset Browser button in level editor toolbar?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleShowToolbarButton),				FCanExecuteAction(),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsShowingToolbarButton)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#endif		auto CreateShowFoldersSubMenu = [this](FMenuBuilder& SubMenuBuilder)		{			SubMenuBuilder.AddMenuEntry(				LOCTEXT("ShowEmptyFoldersOption", "Show Empty Folders"),				LOCTEXT("ShowEmptyFoldersOptionToolTip", "Show empty folders in the view as well as assets?"),				FSlateIcon(),				FUIAction(					FExecuteAction::CreateSP( this, &SExtAssetView::ToggleShowEmptyFolders ),					FCanExecuteAction::CreateSP( this, &SExtAssetView::IsToggleShowEmptyFoldersAllowed ),					FIsActionChecked::CreateSP( this, &SExtAssetView::IsShowingEmptyFolders )				),				NAME_None,				EUserInterfaceActionType::ToggleButton			);		};		MenuBuilder.AddSubMenu(			LOCTEXT("ShowFoldersOption", "Show Folders"),			LOCTEXT("ShowFoldersOptionToolTip", "Show folders in the view as well as assets?"),			FNewMenuDelegate::CreateLambda(CreateShowFoldersSubMenu),			FUIAction(				FExecuteAction::CreateSP( this, &SExtAssetView::ToggleShowFolders ),				FCanExecuteAction::CreateSP( this, &SExtAssetView::IsToggleShowFoldersAllowed ),				FIsActionChecked::CreateSP( this, &SExtAssetView::IsShowingFolders )			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#if ECB_FEA_IGNORE_FOLDERS		MenuBuilder.AddMenuEntry(			LOCTEXT("IgnoreFoldersStartWithDot", "Ignore Folders Start With Dot"),			LOCTEXT("IgnoreFoldersStartWithDotTooltip", "All Folders Start with Dot will be ignored in folder scan process"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateLambda([this, ExtContentBrowserSetting] { ExtContentBrowserSetting->bIgnoreFoldersStartWithDot = !ExtContentBrowserSetting->bIgnoreFoldersStartWithDot; ExtContentBrowserSetting->PostEditChange(); }),				FCanExecuteAction(),				FIsActionChecked::CreateLambda([this, ExtContentBrowserSetting] { return ExtContentBrowserSetting->bIgnoreFoldersStartWithDot; })			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);		MenuBuilder.AddMenuEntry(			LOCTEXT("IgnoreCommonNonContentFolders", "Ignore Common Non-Content Folders"),			LOCTEXT("IgnoreCommonNonContentFoldersTooltip", "Common Non-Content Folders (Binaries, Config,DerivedDataCache, Intermediate, Resources, Saved, Source) will be ignored."),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateLambda([this, ExtContentBrowserSetting] { ExtContentBrowserSetting->bIgnoreCommonNonContentFolders = !ExtContentBrowserSetting->bIgnoreCommonNonContentFolders; ExtContentBrowserSetting->PostEditChange(); }),				FCanExecuteAction(),				FIsActionChecked::CreateLambda([this, ExtContentBrowserSetting] { return ExtContentBrowserSetting->bIgnoreCommonNonContentFolders; })			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);		MenuBuilder.AddMenuEntry(			LOCTEXT("IgnoreExternalContentFolders", "Ignore External Package Content Folders"),			LOCTEXT("IgnoreExternalContentFoldersTooltip", "Common External Package Folders (__ExternalActors__, __ExternalObjects__) will be ignored."),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateLambda([this, ExtContentBrowserSetting] { ExtContentBrowserSetting->bIgnoreExternalContentFolders = !ExtContentBrowserSetting->bIgnoreExternalContentFolders; ExtContentBrowserSetting->PostEditChange(); }),				FCanExecuteAction(),				FIsActionChecked::CreateLambda([this, ExtContentBrowserSetting] { return ExtContentBrowserSetting->bIgnoreExternalContentFolders; })			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);		MenuBuilder.AddMenuEntry(			LOCTEXT("MoreIgnoreFolders", "More Ignore Folders"),			LOCTEXT("MoreIgnoreFoldersTooltip", "Add more ignore folders..."),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateLambda([this, ExtContentBrowserSetting] { ExtContentBrowserSetting->bIgnoreMoreFolders = !ExtContentBrowserSetting->bIgnoreMoreFolders; ExtContentBrowserSetting->PostEditChange(); }),				FCanExecuteAction(),				FIsActionChecked::CreateLambda([this, ExtContentBrowserSetting] { return ExtContentBrowserSetting->bIgnoreMoreFolders; })			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);		TSharedRef<SWidget> IgnoreFoldersWidget = SNew(SEditableTextBox)			.Text_Lambda([this, ExtContentBrowserSetting]()		{			FString IgnoreFolders;			for (int32 Index = 0; Index < ExtContentBrowserSetting->IgnoreFolders.Num(); ++Index)			{				if (Index != 0)				{					IgnoreFolders.Append(TEXT(","));				}				IgnoreFolders += ExtContentBrowserSetting->IgnoreFolders[Index];			}			return FText::FromString(IgnoreFolders);		})			.IsEnabled_Lambda([this, ExtContentBrowserSetting]()		{			return ExtContentBrowserSetting->bIgnoreMoreFolders;		})			//.OnTextChanged_Lambda([this](const FText& InText) {})			.OnTextCommitted_Lambda([this, ExtContentBrowserSetting](const FText& InText, ETextCommit::Type)		{			FString InputText = InText.ToString().TrimStartAndEnd();			if (!InputText.IsEmpty())			{				TArray<FString> Candidates;				InText.ToString().ParseIntoArray(Candidates, TEXT(","));				for (const FString& Candidate : Candidates)				{					FString Trimmed = Candidate.TrimStartAndEnd();					if (!Trimmed.IsEmpty())					{						ExtContentBrowserSetting->IgnoreFolders.AddUnique(Trimmed);					}				}			}			else			{				ExtContentBrowserSetting->IgnoreFolders.Empty();			}			ExtContentBrowserSetting->PostEditChange();		});		MenuBuilder.AddWidget(IgnoreFoldersWidget, FText::GetEmpty());#endif		MenuBuilder.AddMenuEntry(			LOCTEXT("ShowAssetTooltipOption", "Show Tooltip"),			LOCTEXT("ShowAssetTooltipOptionToolTip", "Show the asset tooltip?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleShowAssetTooltip),				FCanExecuteAction(),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsShowingAssetTooltip)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#if ECB_WIP_COLLECTION		MenuBuilder.AddMenuEntry(			LOCTEXT("ShowCollectionOption", "Show Collections"),			LOCTEXT("ShowCollectionOptionToolTip", "Show the collections list in the view?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP( this, &SExtAssetView::ToggleShowCollections ),				FCanExecuteAction::CreateSP( this, &SExtAssetView::IsToggleShowCollectionsAllowed ),				FIsActionChecked::CreateSP( this, &SExtAssetView::IsShowingCollections )			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#endif#if ECB_WIP_FAVORITE		MenuBuilder.AddMenuEntry(			LOCTEXT("ShowFavoriteOptions", "Show Favorites"),			LOCTEXT("ShowFavoriteOptionToolTip", "Show the favorite folders in the view?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleShowFavorites),				FCanExecuteAction::CreateSP(this, &SExtAssetView::IsToggleShowFavoritesAllowed),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsShowingFavorites)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#endif		MenuBuilder.AddMenuEntry(			LOCTEXT("Reset", "Reset"),			LOCTEXT("ResetOptionsTooltip", "Reset view options to default"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateLambda([this, ExtContentBrowserSetting] { ExtContentBrowserSetting->ResetViewSettings(); ExtContentBrowserSetting->PostEditChange(); })				//FCanExecuteAction(),				//FIsActionChecked::CreateLambda([this] { return false; })			),			NAME_None,			EUserInterfaceActionType::Button		);	}	MenuBuilder.EndSection(); // Section: View#if ECB_DEBUG	MenuBuilder.BeginSection("Debug", LOCTEXT("DebugHeading", "Debug"));	{		MenuBuilder.AddWidget(			SNew(SGridPanel)			+ SGridPanel::Slot(0, 0).Padding(3, 0, 0, 0)			[WidgetHelpers::CreateColorWidget(&FExtContentBrowserStyle::CustomContentBrowserBorderBackgroundColor)]			+ SGridPanel::Slot(1, 0).Padding(3, 0, 0, 0)			[WidgetHelpers::CreateColorWidget(&FExtContentBrowserStyle::CustomToolbarBackgroundColor)]			+ SGridPanel::Slot(2, 0).Padding(3, 0, 0, 0)			[WidgetHelpers::CreateColorWidget(&FExtContentBrowserStyle::CustomSourceViewBackgroundColor)]			+ SGridPanel::Slot(3, 0).Padding(3, 0, 0, 0)			[WidgetHelpers::CreateColorWidget(&FExtContentBrowserStyle::CustomAssetViewBackgroundColor)]			, LOCTEXT("Color", "Color")			,/*bNoIndent=*/true		);	}	MenuBuilder.EndSection();#endif#if ECB_TODO	MenuBuilder.BeginSection("Content", LOCTEXT("ContentHeading", "Content"));	{		MenuBuilder.AddMenuEntry(			LOCTEXT("ShowEngineFolderOption", "Show Engine Content"),			LOCTEXT("ShowEngineFolderOptionToolTip", "Show engine content in the view?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP( this, &SExtAssetView::ToggleShowEngineContent ),				FCanExecuteAction(),				FIsActionChecked::CreateSP( this, &SExtAssetView::IsShowingEngineContent )			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);	}	MenuBuilder.EndSection();#endif#if ECB_WIP_SEARCH_RECURSE_TOGGLE	MenuBuilder.BeginSection("Search", LOCTEXT("SearchHeading", "Search"));	{		MenuBuilder.AddMenuEntry(			LOCTEXT("SearchFilterRecursivelyOption", "Search and Filter Recursively"),			LOCTEXT("SearchFilterRecursivelyOptionTooltip", "Search and filter recursively or only in current folder?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleSearchAndFilterRecursively),				FCanExecuteAction(),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsSearchAndFilterRecursively)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#if ECB_LEGACY		MenuBuilder.AddMenuEntry(			LOCTEXT("IncludeClassNameOption", "Search Asset Class Names"),			LOCTEXT("IncludeClassesNameOptionTooltip", "Include asset type names in search criteria?  (e.g. Blueprint, Texture, Sound)"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleIncludeClassNames),				FCanExecuteAction::CreateSP(this, &SExtAssetView::IsToggleIncludeClassNamesAllowed),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsIncludingClassNames)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);		MenuBuilder.AddMenuEntry(			LOCTEXT("IncludeAssetPathOption", "Search Asset Path"),			LOCTEXT("IncludeAssetPathOptionTooltip", "Include entire asset path in search criteria?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleIncludeAssetPaths),				FCanExecuteAction::CreateSP(this, &SExtAssetView::IsToggleIncludeAssetPathsAllowed),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsIncludingAssetPaths)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);		MenuBuilder.AddMenuEntry(			LOCTEXT("IncludeCollectionNameOption", "Search Collection Names"),			LOCTEXT("IncludeCollectionNameOptionTooltip", "Include Collection names in search criteria?"),			FSlateIcon(),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ToggleIncludeCollectionNames),				FCanExecuteAction::CreateSP(this, &SExtAssetView::IsToggleIncludeCollectionNamesAllowed),				FIsActionChecked::CreateSP(this, &SExtAssetView::IsIncludingCollectionNames)			),			NAME_None,			EUserInterfaceActionType::ToggleButton		);#endif	}	MenuBuilder.EndSection();#endif#if ECB_LEGACY // Move outside	MenuBuilder.BeginSection("AssetThumbnails", LOCTEXT("ThumbnailsHeading", "Thumbnails"));	{		MenuBuilder.AddWidget(			SNew(SSlider)				.ToolTipText( LOCTEXT("ThumbnailScaleToolTip", "Adjust the size of thumbnails.") )				.Value( this, &SExtAssetView::GetThumbnailScale )				.OnValueChanged( this, &SExtAssetView::SetThumbnailScale )				.Locked( this, &SExtAssetView::IsThumbnailScalingLocked ),			LOCTEXT("ThumbnailScaleLabel", "Scale"),			/*bNoIndent=*/true			);	}	MenuBuilder.EndSection();#endif	const auto PluginDescriptor = IPluginManager::Get().FindPlugin(TEXT("UAssetBrowser"))->GetDescriptor();	const FText VersionName = FText::FromString(PluginDescriptor.VersionName);	const FText PluginVersion = FText::Format(LOCTEXT("UAssetBrowserVersion", "UAsset Browser {0}"), VersionName);	MenuBuilder.BeginSection("About", LOCTEXT("AboutHeading", "About"));	{		MenuBuilder.AddMenuEntry(			LOCTEXT("Documentation...", "Documentation..."),			LOCTEXT("DocumentationToolTip", "Show documentation in a browser"),			FSlateIcon(FExtContentBrowserStyle::GetStyleSetName(), "UAssetBrowser.Help"),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ShowDocumentation)			),			NAME_None,			EUserInterfaceActionType::Button		);		MenuBuilder.AddMenuEntry(			PluginVersion,			LOCTEXT("ShowChangeLogToolTip", "Show change log"),			FSlateIcon(FExtContentBrowserStyle::GetStyleSetName(), "UAssetBrowser.Icon16x"),			FUIAction(				FExecuteAction::CreateSP(this, &SExtAssetView::ShowPluginVersionChangeLog)			),			NAME_None,			EUserInterfaceActionType::Button		);	}	MenuBuilder.EndSection();	return MenuBuilder.MakeWidget();}void SExtAssetView::ToggleShowFolders(){	check( IsToggleShowFoldersAllowed() );	GetMutableDefault<UExtContentBrowserSettings>()->DisplayFolders = !GetDefault<UExtContentBrowserSettings>()->DisplayFolders;	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();}bool SExtAssetView::IsToggleShowFoldersAllowed() const{	return bCanShowFolders;}bool SExtAssetView::IsShowingFolders() const{	return IsToggleShowFoldersAllowed() && GetDefault<UExtContentBrowserSettings>()->DisplayFolders;}void SExtAssetView::ToggleShowEmptyFolders(){	check( IsToggleShowEmptyFoldersAllowed() );	GetMutableDefault<UExtContentBrowserSettings>()->DisplayEmptyFolders = !GetDefault<UExtContentBrowserSettings>()->DisplayEmptyFolders;	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();}bool SExtAssetView::IsToggleShowEmptyFoldersAllowed() const{	return bCanShowFolders;}bool SExtAssetView::IsShowingEmptyFolders() const{	return IsToggleShowEmptyFoldersAllowed() && GetDefault<UExtContentBrowserSettings>()->DisplayEmptyFolders;}void SExtAssetView::ToggleShowAssetTooltip(){	GetMutableDefault<UExtContentBrowserSettings>()->DisplayAssetTooltip = !GetDefault<UExtContentBrowserSettings>()->DisplayAssetTooltip;}bool SExtAssetView::IsShowingAssetTooltip() const{	return GetDefault<UExtContentBrowserSettings>()->DisplayAssetTooltip;}void SExtAssetView::ShowDocumentation(){	FString Link(TEXT("https://marynate.github.io/uasset-browser/"));	FPlatformProcess::LaunchURL(*Link, nullptr, nullptr);}void SExtAssetView::ShowPluginVersionChangeLog(){	OnRequestShowChangeLog.ExecuteIfBound();}void SExtAssetView::ToggleShowEngineVersionOverlayButton(){	GetMutableDefault<UExtContentBrowserSettings>()->DisplayEngineVersionOverlay = !GetDefault<UExtContentBrowserSettings>()->DisplayEngineVersionOverlay;	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();}bool SExtAssetView::IsShowingEngineVersionOverlayButton() const{	return GetDefault<UExtContentBrowserSettings>()->DisplayEngineVersionOverlay;}void SExtAssetView::ToggleShowContentTypeOverlayButton(){	GetMutableDefault<UExtContentBrowserSettings>()->DisplayContentTypeOverlay = !GetDefault<UExtContentBrowserSettings>()->DisplayContentTypeOverlay;	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();}bool SExtAssetView::IsShowingContentTypeOverlayButton() const{	return GetDefault<UExtContentBrowserSettings>()->DisplayContentTypeOverlay;}void SExtAssetView::ToggleShowValidationStatusOverlayButton(){	GetMutableDefault<UExtContentBrowserSettings>()->DisplayValidationStatusOverlay = !GetDefault<UExtContentBrowserSettings>()->DisplayValidationStatusOverlay;	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();}bool SExtAssetView::IsShowingValidationStatusOverlayButton() const{	return GetDefault<UExtContentBrowserSettings>()->DisplayValidationStatusOverlay;}void SExtAssetView::ToggleShowInvalidAssets(){	GetMutableDefault<UExtContentBrowserSettings>()->DisplayInvalidAssets = !GetDefault<UExtContentBrowserSettings>()->DisplayInvalidAssets;	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();}bool SExtAssetView::IsShowingInvalidAssets() const{	return GetDefault<UExtContentBrowserSettings>()->DisplayInvalidAssets;}void SExtAssetView::ToggleShowToolbarButton(){	FExtContentBrowserSingleton::Get().ToggleShowToolbarButton();}bool SExtAssetView::IsShowingToolbarButton() const{	return GetDefault<UExtContentBrowserSettings>()->DisplayToolbarButton;}#if ECB_LEGACYvoid SExtAssetView::ToggleShowEngineContent(){	bool bDisplayEngine = GetDefault<UExtContentBrowserSettings>()->GetDisplayEngineFolder();	bool bRawDisplayEngine = GetDefault<UExtContentBrowserSettings>()->GetDisplayEngineFolder( true );	// Only if both these flags are false when toggling we want to enable the flag, otherwise we're toggling off	if ( !bDisplayEngine && !bRawDisplayEngine )	{		GetMutableDefault<UExtContentBrowserSettings>()->SetDisplayEngineFolder( true );	}	else	{		GetMutableDefault<UExtContentBrowserSettings>()->SetDisplayEngineFolder( false );		GetMutableDefault<UExtContentBrowserSettings>()->SetDisplayEngineFolder( false, true );	}		GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();}bool SExtAssetView::IsShowingEngineContent() const{	return GetDefault<UExtContentBrowserSettings>()->GetDisplayEngineFolder();}#endifvoid SExtAssetView::ToggleShowCollections(){	const bool bDisplayCollections = GetDefault<UExtContentBrowserSettings>()->GetDisplayCollections();	GetMutableDefault<UExtContentBrowserSettings>()->SetDisplayCollections( !bDisplayCollections );	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();}bool SExtAssetView::IsToggleShowCollectionsAllowed() const{	return bCanShowCollections;}bool SExtAssetView::IsShowingCollections() const{	return IsToggleShowCollectionsAllowed() && GetDefault<UExtContentBrowserSettings>()->GetDisplayCollections();}void SExtAssetView::ToggleShowFavorites(){	const bool bShowingFavorites = GetDefault<UExtContentBrowserSettings>()->GetDisplayFavorites();	GetMutableDefault<UExtContentBrowserSettings>()->SetDisplayFavorites(!bShowingFavorites);	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();}bool SExtAssetView::IsToggleShowFavoritesAllowed() const{	return bCanShowFavorites;}bool SExtAssetView::IsShowingFavorites() const{	return IsToggleShowFavoritesAllowed() && GetDefault<UExtContentBrowserSettings>()->GetDisplayFavorites();}void SExtAssetView::ToggleSearchAndFilterRecursively(){	GetMutableDefault<UExtContentBrowserSettings>()->SearchAndFilterRecursively = !GetDefault<UExtContentBrowserSettings>()->SearchAndFilterRecursively;	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();}bool SExtAssetView::IsSearchAndFilterRecursively() const{#if ECB_WIP_SEARCH_RECURSE_TOGGLE	return GetDefault<UExtContentBrowserSettings>()->SearchAndFilterRecursively;#else	return true;#endif}#if ECB_LEGACYvoid SExtAssetView::ToggleIncludeClassNames(){	const bool bIncludeClassNames = GetDefault<UExtContentBrowserSettings>()->GetIncludeClassNames();	GetMutableDefault<UExtContentBrowserSettings>()->SetIncludeClassNames(!bIncludeClassNames);	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();	OnSearchOptionsChanged.ExecuteIfBound();}bool SExtAssetView::IsToggleIncludeClassNamesAllowed() const{	return true;}bool SExtAssetView::IsIncludingClassNames() const{	return IsToggleIncludeClassNamesAllowed() && GetDefault<UExtContentBrowserSettings>()->GetIncludeClassNames();}void SExtAssetView::ToggleIncludeAssetPaths(){	const bool bIncludeAssetPaths = GetDefault<UExtContentBrowserSettings>()->GetIncludeAssetPaths();	GetMutableDefault<UExtContentBrowserSettings>()->SetIncludeAssetPaths(!bIncludeAssetPaths);	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();	OnSearchOptionsChanged.ExecuteIfBound();}bool SExtAssetView::IsToggleIncludeAssetPathsAllowed() const{	return true;}bool SExtAssetView::IsIncludingAssetPaths() const{	return IsToggleIncludeAssetPathsAllowed() && GetDefault<UExtContentBrowserSettings>()->GetIncludeAssetPaths();}void SExtAssetView::ToggleIncludeCollectionNames(){	const bool bIncludeCollectionNames = GetDefault<UExtContentBrowserSettings>()->GetIncludeCollectionNames();	GetMutableDefault<UExtContentBrowserSettings>()->SetIncludeCollectionNames(!bIncludeCollectionNames);	GetMutableDefault<UExtContentBrowserSettings>()->PostEditChange();	OnSearchOptionsChanged.ExecuteIfBound();}bool SExtAssetView::IsToggleIncludeCollectionNamesAllowed() const{	return true;}bool SExtAssetView::IsIncludingCollectionNames() const{	return IsToggleIncludeCollectionNamesAllowed() && GetDefault<UExtContentBrowserSettings>()->GetIncludeCollectionNames();}#endifvoid SExtAssetView::SetCurrentViewType(EAssetViewType::Type NewType){	if ( ensure(NewType != EAssetViewType::MAX) && NewType != CurrentViewType )	{		//ResetQuickJump();		CurrentViewType = NewType;		CreateCurrentView();		SyncToSelection();		// Clear relevant thumbnails to render fresh ones in the new view if needed		RelevantThumbnails.Reset();		VisibleItems.Reset();		if ( NewType == EAssetViewType::Tile )		{			CurrentThumbnailSize = TileViewThumbnailSize;			bPendingUpdateThumbnails = true;		}		else if ( NewType == EAssetViewType::List )		{			CurrentThumbnailSize = ListViewThumbnailSize;			bPendingUpdateThumbnails = true;		}		else if ( NewType == EAssetViewType::Column )		{			// No thumbnails, but we do need to refresh filtered items to determine a majority asset type			MajorityAssetType = NAME_None;			RefreshFilteredItems();			RefreshFolders();			SortList();		}	}}void SExtAssetView::SetCurrentViewTypeFromMenu(EAssetViewType::Type NewType){	if (NewType != CurrentViewType)	{		SetCurrentViewType(NewType);		FSlateApplication::Get().DismissAllMenus();	}}void SExtAssetView::CreateCurrentView(){	TileView.Reset();	ListView.Reset();	ColumnView.Reset();	TSharedRef<SWidget> NewView = SNullWidget::NullWidget;	switch (CurrentViewType)	{		case EAssetViewType::Tile:			TileView = CreateTileView();			NewView = CreateShadowOverlay(TileView.ToSharedRef());			break;		case EAssetViewType::List:			ListView = CreateListView();			NewView = CreateShadowOverlay(ListView.ToSharedRef());			break;		case EAssetViewType::Column:			ColumnView = CreateColumnView();			NewView = CreateShadowOverlay(ColumnView.ToSharedRef());			break;	}		ViewContainer->SetContent( NewView );}TSharedRef<SWidget> SExtAssetView::CreateShadowOverlay( TSharedRef<STableViewBase> Table ){	return SNew(SScrollBorder, Table)		[			Table		];}EAssetViewType::Type SExtAssetView::GetCurrentViewType() const{	return CurrentViewType;}bool SExtAssetView::IsCurrentViewType(EAssetViewType::Type ViewType) const{	return GetCurrentViewType() == ViewType;}void SExtAssetView::FocusList() const{	switch ( GetCurrentViewType() )	{		case EAssetViewType::List: FSlateApplication::Get().SetKeyboardFocus(ListView, EFocusCause::SetDirectly); break;		case EAssetViewType::Tile: FSlateApplication::Get().SetKeyboardFocus(TileView, EFocusCause::SetDirectly); break;		case EAssetViewType::Column: FSlateApplication::Get().SetKeyboardFocus(ColumnView, EFocusCause::SetDirectly); break;	}}void SExtAssetView::RefreshList(){	switch ( GetCurrentViewType() )	{		case EAssetViewType::List: ListView->RequestListRefresh(); break;		case EAssetViewType::Tile: TileView->RequestListRefresh(); break;		case EAssetViewType::Column: ColumnView->RequestListRefresh(); break;	}}void SExtAssetView::SetSelection(const TSharedPtr<FExtAssetViewItem>& Item){	switch ( GetCurrentViewType() )	{		case EAssetViewType::List: ListView->SetSelection(Item); break;		case EAssetViewType::Tile: TileView->SetSelection(Item); break;		case EAssetViewType::Column: ColumnView->SetSelection(Item); break;	}}void SExtAssetView::SetItemSelection(const TSharedPtr<FExtAssetViewItem>& Item, bool bSelected, const ESelectInfo::Type SelectInfo){	switch ( GetCurrentViewType() )	{		case EAssetViewType::List: ListView->SetItemSelection(Item, bSelected, SelectInfo); break;		case EAssetViewType::Tile: TileView->SetItemSelection(Item, bSelected, SelectInfo); break;		case EAssetViewType::Column: ColumnView->SetItemSelection(Item, bSelected, SelectInfo); break;	}}void SExtAssetView::RequestScrollIntoView(const TSharedPtr<FExtAssetViewItem>& Item){	switch ( GetCurrentViewType() )	{		case EAssetViewType::List: ListView->RequestScrollIntoView(Item); break;		case EAssetViewType::Tile: TileView->RequestScrollIntoView(Item); break;		case EAssetViewType::Column: ColumnView->RequestScrollIntoView(Item); break;	}}#if ECB_TODOvoid SExtAssetView::OnOpenAssetsOrFolders(){	TArray<FAssetData> SelectedAssets = GetSelectedAssets();	TArray<FString> SelectedFolders = GetSelectedFolders();	if (SelectedAssets.Num() > 0 && SelectedFolders.Num() == 0)	{		OnAssetsActivated.ExecuteIfBound(SelectedAssets, EAssetTypeActivationMethod::Opened);	}	else if (SelectedAssets.Num() == 0 && SelectedFolders.Num() > 0)	{		OnPathSelected.ExecuteIfBound(SelectedFolders[0]);	}}void SExtAssetView::OnPreviewAssets(){	OnAssetsActivated.ExecuteIfBound(GetSelectedAssets(), EAssetTypeActivationMethod::Previewed);}#endifvoid SExtAssetView::ClearSelection(bool bForceSilent){	const bool bTempBulkSelectingValue = bForceSilent ? true : bBulkSelecting;	TGuardValue<bool>(bBulkSelecting, bTempBulkSelectingValue);	switch ( GetCurrentViewType() )	{		case EAssetViewType::List: ListView->ClearSelection(); break;		case EAssetViewType::Tile: TileView->ClearSelection(); break;		case EAssetViewType::Column: ColumnView->ClearSelection(); break;	}}TSharedRef<ITableRow> SExtAssetView::MakeListViewWidget(TSharedPtr<FExtAssetViewItem> AssetItem, const TSharedRef<STableViewBase>& OwnerTable){	if ( !ensure(AssetItem.IsValid()) )	{		return SNew( STableRow<TSharedPtr<FExtAssetViewAsset>>, OwnerTable );	}	VisibleItems.Add(AssetItem);	bPendingUpdateThumbnails = true;	if(AssetItem->GetType() == EExtAssetItemType::Folder)	{		TSharedPtr< STableRow<TSharedPtr<FExtAssetViewItem>> > TableRowWidget;		SAssignNew( TableRowWidget, STableRow<TSharedPtr<FExtAssetViewItem>>, OwnerTable )			.Style(FAppStyle::Get(), "ContentBrowser.AssetListView.ColumnListTableRow")			.Cursor( bAllowDragging ? EMouseCursor::GrabHand : EMouseCursor::Default )			.OnDragDetected( this, &SExtAssetView::OnDraggingAssetItem );		TSharedRef<SExtAssetListItem> Item =			SNew(SExtAssetListItem)			.AssetItem(AssetItem)			.ItemHeight(this, &SExtAssetView::GetListViewItemHeight)			.OnItemDestroyed(this, &SExtAssetView::AssetItemWidgetDestroyed)			.ShouldAllowToolTip(this, &SExtAssetView::ShouldAllowToolTips)			.HighlightText(HighlightedText)			.IsSelected(FIsSelected::CreateSP(TableRowWidget.Get(), &STableRow<TSharedPtr<FExtAssetViewItem>>::IsSelectedExclusively));			//.OnAssetsOrPathsDragDropped(this, &SExtAssetView::OnAssetsOrPathsDragDropped)			//.OnFilesDragDropped(this, &SExtAssetView::OnFilesDragDropped);		TableRowWidget->SetContent(Item);		return TableRowWidget.ToSharedRef();	}	else	{		TSharedPtr<FExtAssetViewAsset> AssetItemAsAsset = StaticCastSharedPtr<FExtAssetViewAsset>(AssetItem);		TSharedPtr<FExtAssetThumbnail>* AssetThumbnailPtr = RelevantThumbnails.Find(AssetItemAsAsset);		TSharedPtr<FExtAssetThumbnail> AssetThumbnail;		if ( AssetThumbnailPtr )		{			AssetThumbnail = *AssetThumbnailPtr;		}		else		{			const float ThumbnailResolution = ListViewThumbnailResolution;			AssetThumbnail = MakeShareable( new FExtAssetThumbnail( AssetItemAsAsset->Data, ThumbnailResolution, ThumbnailResolution, AssetThumbnailPool ) );			RelevantThumbnails.Add( AssetItemAsAsset, AssetThumbnail );			AssetThumbnail->GetViewportRenderTargetTexture(); // Access the texture once to trigger it to render		}		TSharedPtr< STableRow<TSharedPtr<FExtAssetViewItem>> > TableRowWidget;		SAssignNew( TableRowWidget, STableRow<TSharedPtr<FExtAssetViewItem>>, OwnerTable )		.Style(FAppStyle::Get(), "ContentBrowser.AssetListView.ColumnListTableRow")		.Cursor( bAllowDragging ? EMouseCursor::GrabHand : EMouseCursor::Default )		.OnDragDetected( this, &SExtAssetView::OnDraggingAssetItem );		TSharedRef<SExtAssetListItem> Item =			SNew(SExtAssetListItem)			.AssetThumbnail(AssetThumbnail)			.AssetItem(AssetItem)			.ThumbnailPadding(ListViewThumbnailPadding)			.ItemHeight(this, &SExtAssetView::GetListViewItemHeight)			.OnItemDestroyed(this, &SExtAssetView::AssetItemWidgetDestroyed)			.ShouldAllowToolTip(this, &SExtAssetView::ShouldAllowToolTips)			.HighlightText(HighlightedText)			.ThumbnailLabel(ThumbnailLabel)			.ThumbnailHintColorAndOpacity(this, &SExtAssetView::GetThumbnailHintColorAndOpacity)			.AllowThumbnailHintLabel(AllowThumbnailHintLabel)			.IsSelected(FIsSelected::CreateSP(TableRowWidget.Get(), &STableRow<TSharedPtr<FExtAssetViewItem>>::IsSelectedExclusively));			//.OnIsAssetValidForCustomToolTip(OnIsAssetValidForCustomToolTip)			//.OnGetCustomAssetToolTip(OnGetCustomAssetToolTip)			//.OnVisualizeAssetToolTip(OnVisualizeAssetToolTip)			//.OnAssetToolTipClosing(OnAssetToolTipClosing);		TableRowWidget->SetContent(Item);		return TableRowWidget.ToSharedRef();	}}TSharedRef<ITableRow> SExtAssetView::MakeTileViewWidget(TSharedPtr<FExtAssetViewItem> AssetItem, const TSharedRef<STableViewBase>& OwnerTable){	if ( !ensure(AssetItem.IsValid()) )	{		return SNew( STableRow<TSharedPtr<FExtAssetViewAsset>>, OwnerTable );	}	VisibleItems.Add(AssetItem);	bPendingUpdateThumbnails = true;	if(AssetItem->GetType() == EExtAssetItemType::Folder)	{		TSharedPtr< STableRow<TSharedPtr<FExtAssetViewItem>> > TableRowWidget;		SAssignNew( TableRowWidget, STableRow<TSharedPtr<FExtAssetViewItem>>, OwnerTable )			.Style( FAppStyle::Get(), "ContentBrowser.AssetListView.ColumnListTableRow" )			.Cursor( bAllowDragging ? EMouseCursor::GrabHand : EMouseCursor::Default )			.OnDragDetected( this, &SExtAssetView::OnDraggingAssetItem );		TSharedRef<SExtAssetTileItem> Item =			SNew(SExtAssetTileItem)			.AssetItem(AssetItem)			.ItemWidth(this, &SExtAssetView::GetTileViewItemWidth)			.OnItemDestroyed(this, &SExtAssetView::AssetItemWidgetDestroyed)			.ShouldAllowToolTip(this, &SExtAssetView::ShouldAllowToolTips)			.HighlightText(HighlightedText)			.IsSelected(FIsSelected::CreateSP(TableRowWidget.Get(), &STableRow<TSharedPtr<FExtAssetViewItem>>::IsSelectedExclusively));//			.OnAssetsOrPathsDragDropped(this, &SExtAssetView::OnAssetsOrPathsDragDropped)//			.OnFilesDragDropped(this, &SExtAssetView::OnFilesDragDropped);		TableRowWidget->SetContent(Item);		return TableRowWidget.ToSharedRef();	}	else	{		TSharedPtr<FExtAssetViewAsset> AssetItemAsAsset = StaticCastSharedPtr<FExtAssetViewAsset>(AssetItem);		TSharedPtr<FExtAssetThumbnail>* AssetThumbnailPtr = RelevantThumbnails.Find(AssetItemAsAsset);		TSharedPtr<FExtAssetThumbnail> AssetThumbnail;		if ( AssetThumbnailPtr )		{			AssetThumbnail = *AssetThumbnailPtr;		}		else		{			const float ThumbnailResolution = TileViewThumbnailResolution;			AssetThumbnail = MakeShareable( new FExtAssetThumbnail( AssetItemAsAsset->Data, ThumbnailResolution, ThumbnailResolution, AssetThumbnailPool ) );			RelevantThumbnails.Add( AssetItemAsAsset, AssetThumbnail );			AssetThumbnail->GetViewportRenderTargetTexture(); // Access the texture once to trigger it to render		}		TSharedPtr< STableRow<TSharedPtr<FExtAssetViewItem>> > TableRowWidget;		SAssignNew( TableRowWidget, STableRow<TSharedPtr<FExtAssetViewItem>>, OwnerTable )		.Style(FAppStyle::Get(), "ContentBrowser.AssetListView.ColumnListTableRow")		.Cursor( bAllowDragging ? EMouseCursor::GrabHand : EMouseCursor::Default )		.OnDragDetected( this, &SExtAssetView::OnDraggingAssetItem );		TSharedRef<SExtAssetTileItem> Item =			SNew(SExtAssetTileItem)			.AssetThumbnail(AssetThumbnail)			.AssetItem(AssetItem)			.ThumbnailPadding(TileViewThumbnailPadding)			.ItemWidth(this, &SExtAssetView::GetTileViewItemWidth)			.OnItemDestroyed(this, &SExtAssetView::AssetItemWidgetDestroyed)			.ShouldAllowToolTip(this, &SExtAssetView::ShouldAllowToolTips)			.HighlightText(HighlightedText)			.ThumbnailLabel(ThumbnailLabel)			.ThumbnailHintColorAndOpacity(this, &SExtAssetView::GetThumbnailHintColorAndOpacity)			.AllowThumbnailHintLabel(AllowThumbnailHintLabel)			.IsSelected(FIsSelected::CreateSP(TableRowWidget.Get(), &STableRow<TSharedPtr<FExtAssetViewItem>>::IsSelectedExclusively));			//.OnIsAssetValidForCustomToolTip(OnIsAssetValidForCustomToolTip)			//.OnGetCustomAssetToolTip(OnGetCustomAssetToolTip)			//.OnVisualizeAssetToolTip( OnVisualizeAssetToolTip )			//.OnAssetToolTipClosing( OnAssetToolTipClosing );		TableRowWidget->SetContent(Item);		return TableRowWidget.ToSharedRef();	}}TSharedRef<ITableRow> SExtAssetView::MakeColumnViewWidget(TSharedPtr<FExtAssetViewItem> AssetItem, const TSharedRef<STableViewBase>& OwnerTable){	if ( !ensure(AssetItem.IsValid()) )	{		return SNew( STableRow<TSharedPtr<FExtAssetViewItem>>, OwnerTable )			.Style(FAppStyle::Get(), "ContentBrowser.AssetListView.ColumnListTableRow");	}	// Update the cached custom data	AssetItem->CacheCustomColumns(CustomColumns, false, true, false);		return		SNew( SExtAssetColumnViewRow, OwnerTable )		.OnDragDetected( this, &SExtAssetView::OnDraggingAssetItem )		.Cursor( bAllowDragging ? EMouseCursor::GrabHand : EMouseCursor::Default )		.AssetColumnItem(			SNew(SExtAssetColumnItem)				.AssetItem(AssetItem)				.OnItemDestroyed(this, &SExtAssetView::AssetItemWidgetDestroyed)				.HighlightText( HighlightedText )				//.OnAssetsOrPathsDragDropped(this, &SExtAssetView::OnAssetsOrPathsDragDropped)				//.OnFilesDragDropped(this, &SExtAssetView::OnFilesDragDropped)				//.OnIsAssetValidForCustomToolTip(OnIsAssetValidForCustomToolTip)				//.OnGetCustomAssetToolTip(OnGetCustomAssetToolTip)				//.OnVisualizeAssetToolTip( OnVisualizeAssetToolTip )				//.OnAssetToolTipClosing( OnAssetToolTipClosing )		);}void SExtAssetView::AssetItemWidgetDestroyed(const TSharedPtr<FExtAssetViewItem>& Item){	if ( VisibleItems.Remove(Item) != INDEX_NONE )	{		bPendingUpdateThumbnails = true;	}}void SExtAssetView::UpdateThumbnails(){	int32 MinItemIdx = INDEX_NONE;	int32 MaxItemIdx = INDEX_NONE;	int32 MinVisibleItemIdx = INDEX_NONE;	int32 MaxVisibleItemIdx = INDEX_NONE;	const int32 HalfNumOffscreenThumbnails = NumOffscreenThumbnails * 0.5;	for ( auto ItemIt = VisibleItems.CreateConstIterator(); ItemIt; ++ItemIt )	{		int32 ItemIdx = FilteredAssetItems.Find(*ItemIt);		if ( ItemIdx != INDEX_NONE )		{			const int32 ItemIdxLow = FMath::Max<int32>(0, ItemIdx - HalfNumOffscreenThumbnails);			const int32 ItemIdxHigh = FMath::Min<int32>(FilteredAssetItems.Num() - 1, ItemIdx + HalfNumOffscreenThumbnails);			if ( MinItemIdx == INDEX_NONE || ItemIdxLow < MinItemIdx )			{				MinItemIdx = ItemIdxLow;			}			if ( MaxItemIdx == INDEX_NONE || ItemIdxHigh > MaxItemIdx )			{				MaxItemIdx = ItemIdxHigh;			}			if ( MinVisibleItemIdx == INDEX_NONE || ItemIdx < MinVisibleItemIdx )			{				MinVisibleItemIdx = ItemIdx;			}			if ( MaxVisibleItemIdx == INDEX_NONE || ItemIdx > MaxVisibleItemIdx )			{				MaxVisibleItemIdx = ItemIdx;			}		}	}	if ( MinItemIdx != INDEX_NONE && MaxItemIdx != INDEX_NONE && MinVisibleItemIdx != INDEX_NONE && MaxVisibleItemIdx != INDEX_NONE )	{		// We have a new min and a new max, compare it to the old min and max so we can create new thumbnails		// when appropriate and remove old thumbnails that are far away from the view area.		TMap< TSharedPtr<FExtAssetViewAsset>, TSharedPtr<FExtAssetThumbnail> > NewRelevantThumbnails;		// Operate on offscreen items that are furthest away from the visible items first since the thumbnail pool processes render requests in a LIFO order.		while (MinItemIdx < MinVisibleItemIdx || MaxItemIdx > MaxVisibleItemIdx)		{			const int32 LowEndDistance = MinVisibleItemIdx - MinItemIdx;			const int32 HighEndDistance = MaxItemIdx - MaxVisibleItemIdx;			if ( HighEndDistance > LowEndDistance )			{				if(FilteredAssetItems.IsValidIndex(MaxItemIdx) && FilteredAssetItems[MaxItemIdx]->GetType() != EExtAssetItemType::Folder)				{					AddItemToNewThumbnailRelevancyMap( StaticCastSharedPtr<FExtAssetViewAsset>(FilteredAssetItems[MaxItemIdx]), NewRelevantThumbnails );				}				MaxItemIdx--;			}			else			{				if(FilteredAssetItems.IsValidIndex(MinItemIdx) && FilteredAssetItems[MinItemIdx]->GetType() != EExtAssetItemType::Folder)				{					AddItemToNewThumbnailRelevancyMap( StaticCastSharedPtr<FExtAssetViewAsset>(FilteredAssetItems[MinItemIdx]), NewRelevantThumbnails );				}				MinItemIdx++;			}		}		// Now operate on VISIBLE items then prioritize them so they are rendered first		TArray< TSharedPtr<FExtAssetThumbnail> > ThumbnailsToPrioritize;		for ( int32 ItemIdx = MinVisibleItemIdx; ItemIdx <= MaxVisibleItemIdx; ++ItemIdx )		{			if(FilteredAssetItems.IsValidIndex(ItemIdx) && FilteredAssetItems[ItemIdx]->GetType() != EExtAssetItemType::Folder)			{				TSharedPtr<FExtAssetThumbnail> Thumbnail = AddItemToNewThumbnailRelevancyMap( StaticCastSharedPtr<FExtAssetViewAsset>(FilteredAssetItems[ItemIdx]), NewRelevantThumbnails );				if ( Thumbnail.IsValid() )				{					ThumbnailsToPrioritize.Add(Thumbnail);				}			}		}		// Now prioritize all thumbnails there were in the visible range		if ( ThumbnailsToPrioritize.Num() > 0 )		{			AssetThumbnailPool->PrioritizeThumbnails(ThumbnailsToPrioritize, CurrentThumbnailSize, CurrentThumbnailSize);		}		// Assign the new map of relevant thumbnails. This will remove any entries that were no longer relevant.		RelevantThumbnails = NewRelevantThumbnails;	}}TSharedPtr<FExtAssetThumbnail> SExtAssetView::AddItemToNewThumbnailRelevancyMap(const TSharedPtr<FExtAssetViewAsset>& Item, TMap< TSharedPtr<FExtAssetViewAsset>, TSharedPtr<FExtAssetThumbnail> >& NewRelevantThumbnails){	const TSharedPtr<FExtAssetThumbnail>* Thumbnail = RelevantThumbnails.Find(Item);	if ( Thumbnail )	{		// The thumbnail is still relevant, add it to the new list		NewRelevantThumbnails.Add(Item, *Thumbnail);		return *Thumbnail;	}	else	{		if ( !ensure(CurrentThumbnailSize > 0 && CurrentThumbnailSize <= MAX_THUMBNAIL_SIZE) )		{			// Thumbnail size must be in a sane range			CurrentThumbnailSize = 64;		}		// The thumbnail newly relevant, create a new thumbnail		const float ThumbnailResolution = CurrentThumbnailSize * MaxThumbnailScale;		TSharedPtr<FExtAssetThumbnail> NewThumbnail = MakeShareable( new FExtAssetThumbnail( Item->Data, ThumbnailResolution, ThumbnailResolution, AssetThumbnailPool ) );		NewRelevantThumbnails.Add( Item, NewThumbnail );		NewThumbnail->GetViewportRenderTargetTexture(); // Access the texture once to trigger it to render		return NewThumbnail;	}}void SExtAssetView::AssetSelectionChanged( TSharedPtr< struct FExtAssetViewItem > AssetItem, ESelectInfo::Type SelectInfo ){	if ( !bBulkSelecting )	{		if ( AssetItem.IsValid() && AssetItem->GetType() != EExtAssetItemType::Folder )		{			OnAssetSelected.ExecuteIfBound(StaticCastSharedPtr<FExtAssetViewAsset>(AssetItem)->Data);		}		else		{			OnAssetSelected.ExecuteIfBound(FExtAssetData());		}	}}void SExtAssetView::ItemScrolledIntoView(TSharedPtr<struct FExtAssetViewItem> AssetItem, const TSharedPtr<ITableRow>& Widget ){	const bool bTryRestoreFocus = false; //if ( AssetItem->bRenameWhenScrolledIntoview )	if (bTryRestoreFocus)	{		// Make sure we have window focus to avoid the inline text editor from canceling itself if we try to click on it		// This can happen if creating an asset opens an intermediary window which steals our focus, 		// eg, the blueprint and slate widget style class windows (TTP# 314240)		TSharedPtr<SWindow> OwnerWindow = FSlateApplication::Get().FindWidgetWindow(AsShared());		if(OwnerWindow.IsValid())		{			OwnerWindow->BringToFront();		}		//AwaitingRename = AssetItem;	}}TSharedPtr<SWidget> SExtAssetView::OnGetContextMenuContent(){	if ( CanOpenContextMenu() )	{		const TArray<FString> SelectedFolders = GetSelectedFolders();		if(SelectedFolders.Num() > 0)		{			return NULL;// OnGetFolderContextMenu.Execute(SelectedFolders, OnGetPathContextMenuExtender);		}		else		{			return OnGetAssetContextMenu.Execute(GetSelectedAssets());		}	}	return NULL;}bool SExtAssetView::CanOpenContextMenu() const{#if ECB_LEGACY	if ( !OnGetAssetContextMenu.IsBound() )	{		// You can only a summon a context menu if one is set up		return false;	}#endif	TArray<FExtAssetData> SelectedAssets = GetSelectedAssets();#if ECB_LEGACY	// Detect if at least one temporary item was selected. If there were no valid assets selected and a temporary one was, then deny the context menu.	TArray<TSharedPtr<FExtAssetViewItem>> SelectedItems = GetSelectedItems();	bool bAtLeastOneTemporaryItemFound = false;	for ( auto ItemIt = SelectedItems.CreateConstIterator(); ItemIt; ++ItemIt )	{		const TSharedPtr<FExtAssetViewItem>& Item = *ItemIt;		if ( Item->IsTemporaryItem() )		{			bAtLeastOneTemporaryItemFound = true;		}	}	// If there were no valid assets found, but some invalid assets were found, deny the context menu	if ( SelectedAssets.Num() == 0 && bAtLeastOneTemporaryItemFound )	{		return false;	}	if ( SelectedAssets.Num() == 0 && SourcesData.HasCollections() )	{		// Don't allow a context menu when we're viewing a collection and have no assets selected		return false;	}	// Build a list of selected object paths	TArray<FString> ObjectPaths;	for(auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt)	{		ObjectPaths.Add( AssetIt->ObjectPath.ToString() );	}	bool bLoadSuccessful = true;	if ( bPreloadAssetsForContextMenu )	{		TArray<UObject*> LoadedObjects;		const bool bAllowedToPrompt = false;		bLoadSuccessful = ContentBrowserUtils::LoadAssetsIfNeeded(ObjectPaths, LoadedObjects, bAllowedToPrompt);	}	// Do not show the context menu if the load failed	return bLoadSuccessful;#endif	return SelectedAssets.Num() > 0;}void SExtAssetView::OnListMouseButtonDoubleClick(TSharedPtr<FExtAssetViewItem> AssetItem){	if ( !ensure(AssetItem.IsValid()) )	{		return;	}	if ( AssetItem->GetType() == EExtAssetItemType::Folder )	{		OnPathSelected.ExecuteIfBound(StaticCastSharedPtr<FExtAssetViewFolder>(AssetItem)->FolderPath);		return;	}	if ( AssetItem->IsTemporaryItem() )	{		// You may not activate temporary items, they are just for display.		return;	}#if ECB_LEGACY	TArray<FExtAssetData> ActivatedAssets;	ActivatedAssets.Add(StaticCastSharedPtr<FExtAssetViewAsset>(AssetItem)->Data);	OnAssetsActivated.ExecuteIfBound( ActivatedAssets, EAssetTypeActivationMethod::DoubleClicked );#endif}FReply SExtAssetView::OnDraggingAssetItem( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ){	if (bAllowDragging)	{		TArray<FExtAssetData> DraggedAssets;		TArray<FString> DraggedAssetPaths;		//TArray<FString> DraggedAssetFilePaths;		TArray<FAssetData> FakeDraggedAssets;		// Work out which assets to drag		{			TArray<FExtAssetData> AssetDataList = GetSelectedAssets();			for (const FExtAssetData& AssetData : AssetDataList)			{				// Skip invalid assets and redirectors				if (AssetData.IsValid() && AssetData.AssetClass != UObjectRedirector::StaticClass()->GetFName())				{					//DraggedAssets.Add(AssetData.AssetData);					if (!FExtContentBrowserSingleton::GetAssetRegistry().IsCachedDependencyInfoInValid(AssetData))					{						DraggedAssets.Add(AssetData);						//DraggedAssetFilePaths.Add(AssetData.PackageFilePath.ToString());						FakeDraggedAssets.Add(AssetData.AssetData);					}				}			}		}#if ECB_DISABLE // no support of dragging a whole external folder around, yet		// Work out which asset paths to drag		{			TArray<FString> SelectedFolders = GetSelectedFolders();			if (SelectedFolders.Num() > 0 && !SourcesData.HasCollections())			{				DraggedAssetPaths = MoveTemp(SelectedFolders);			}		}#endif#if ECB_DISABLE		// Use the custom drag handler?		if (DraggedAssets.Num() > 0 && FEditorDelegates::OnAssetDragStarted.IsBound())		{			FEditorDelegates::OnAssetDragStarted.Broadcast(DraggedAssets, nullptr);			return FReply::Handled();		}#endif				// Use the standard drag handler?		if ((DraggedAssets.Num() > 0 || DraggedAssetPaths.Num() > 0) && MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton))		{			if (MouseEvent.IsControlDown())			{				//return FReply::Handled().BeginDragDrop(FAssetDragDropOp::New(MoveTemp(FakeDraggedAssets), MoveTemp(DraggedAssetPaths)));				return FReply::Handled().BeginDragDrop(FExtAssetDragDropOp::New(MoveTemp(DraggedAssets), MoveTemp(DraggedAssetPaths)));			}			else			{				return FReply::Handled().BeginDragDrop(FExtAssetDragDropOp::New(MoveTemp(DraggedAssets), MoveTemp(DraggedAssetPaths)));			}		}	}	return FReply::Unhandled();}bool SExtAssetView::ShouldAllowToolTips() const{	if (!IsShowingAssetTooltip())	{		return false;	}	bool bIsRightClickScrolling = false;	switch( CurrentViewType )	{		case EAssetViewType::List:			bIsRightClickScrolling = ListView->IsRightClickScrolling();			break;		case EAssetViewType::Tile:			bIsRightClickScrolling = TileView->IsRightClickScrolling();			break;		case EAssetViewType::Column:			bIsRightClickScrolling = ColumnView->IsRightClickScrolling();			break;		default:			bIsRightClickScrolling = false;			break;	}	return !bIsRightClickScrolling;}FText SExtAssetView::GetAssetCountText() const{	const int32 NumAssets = FilteredAssetItems.Num();	const int32 NumSelectedAssets = GetSelectedItems().Num();	FText AssetCount = FText::GetEmpty();	if ( NumSelectedAssets == 0 )	{		if ( NumAssets == 1 )		{			AssetCount = LOCTEXT("AssetCountLabelSingular", "1 item");		}		else		{			AssetCount = FText::Format( LOCTEXT("AssetCountLabelPlural", "{0} items"), FText::AsNumber(NumAssets) );		}	}	else	{		if ( NumAssets == 1 )		{			AssetCount = FText::Format( LOCTEXT("AssetCountLabelSingularPlusSelection", "1 item ({0} selected)"), FText::AsNumber(NumSelectedAssets) );		}		else		{			AssetCount = FText::Format( LOCTEXT("AssetCountLabelPluralPlusSelection", "{0} items ({1} selected)"), FText::AsNumber(NumAssets), FText::AsNumber(NumSelectedAssets) );		}	}	return AssetCount;}EVisibility SExtAssetView::GetListViewVisibility() const{	return GetCurrentViewType() == EAssetViewType::List ? EVisibility::Visible : EVisibility::Collapsed;}EVisibility SExtAssetView::GetTileViewVisibility() const{	return GetCurrentViewType() == EAssetViewType::Tile ? EVisibility::Visible : EVisibility::Collapsed;}EVisibility SExtAssetView::GetColumnViewVisibility() const{	return GetCurrentViewType() == EAssetViewType::Column ? EVisibility::Visible : EVisibility::Collapsed;}#if ECB_LEGACYvoid SExtAssetView::ToggleThumbnailEditMode(){	bThumbnailEditMode = !bThumbnailEditMode;}#endiffloat SExtAssetView::GetThumbnailScale() const{	return ThumbnailScaleSliderValue.Get();}void SExtAssetView::SetThumbnailScale( float NewValue ){	ThumbnailScaleSliderValue = NewValue;	RefreshList();}bool SExtAssetView::IsThumbnailScalingLocked() const{	return GetCurrentViewType() == EAssetViewType::Column;}float SExtAssetView::GetListViewItemHeight() const{	return (ListViewThumbnailSize + ListViewThumbnailPadding * 2) * FMath::Lerp(MinThumbnailScale, MaxThumbnailScale, GetThumbnailScale());}float SExtAssetView::GetTileViewItemHeight() const{	return TileViewNameHeight + GetTileViewItemBaseHeight() * FillScale;}float SExtAssetView::GetTileViewItemBaseHeight() const{	return (TileViewThumbnailSize + TileViewThumbnailPadding * 2) * FMath::Lerp(MinThumbnailScale, MaxThumbnailScale, GetThumbnailScale());}float SExtAssetView::GetTileViewItemWidth() const{	return GetTileViewItemBaseWidth() * FillScale;}float SExtAssetView::GetTileViewItemBaseWidth() const //-V524{	return ( TileViewThumbnailSize + TileViewThumbnailPadding * 2 ) * FMath::Lerp( MinThumbnailScale, MaxThumbnailScale, GetThumbnailScale() );}EColumnSortMode::Type SExtAssetView::GetColumnSortMode(const FName ColumnId) const{	for (int32 PriorityIdx = 0; PriorityIdx < EColumnSortPriority::Max; PriorityIdx++)	{		const EColumnSortPriority::Type SortPriority = static_cast<EColumnSortPriority::Type>(PriorityIdx);		if (ColumnId == SortManager.GetSortColumnId(SortPriority))		{			return SortManager.GetSortMode(SortPriority);		}	}	return EColumnSortMode::None;}EColumnSortPriority::Type SExtAssetView::GetColumnSortPriority(const FName ColumnId) const{	for (int32 PriorityIdx = 0; PriorityIdx < EColumnSortPriority::Max; PriorityIdx++)	{		const EColumnSortPriority::Type SortPriority = static_cast<EColumnSortPriority::Type>(PriorityIdx);		if (ColumnId == SortManager.GetSortColumnId(SortPriority))		{			return SortPriority;		}	}	return EColumnSortPriority::Primary;}void SExtAssetView::OnSortColumnHeader(const EColumnSortPriority::Type SortPriority, const FName& ColumnId, const EColumnSortMode::Type NewSortMode){	SortManager.SetSortColumnId(SortPriority, ColumnId);	SortManager.SetSortMode(SortPriority, NewSortMode);	SortList();}EVisibility SExtAssetView::IsAssetShowWarningTextVisible() const{	return (FilteredAssetItems.Num() > 0 || bQuickFrontendListRefreshRequested) ? EVisibility::Collapsed : EVisibility::HitTestInvisible;}FText SExtAssetView::GetAssetShowWarningText() const{	if (AssetShowWarningText.IsSet())	{		return AssetShowWarningText.Get();	}		FText NothingToShowText, DropText;	if (ShouldFilterRecursively())	{		NothingToShowText = LOCTEXT( "NothingToShowCheckFilter", "No results, check your filter." );	}#if ECB_WIP_COLLECTION	if ( SourcesData.HasCollections() && !SourcesData.IsDynamicCollection() )	{		DropText = LOCTEXT( "DragAssetsHere", "Drag and drop assets here to add them to the collection." );	}	else if ( OnGetAssetContextMenu.IsBound() )	{		DropText = LOCTEXT( "DropFilesOrRightClick", "Drop files here or right click to create content." );	}#endif		return NothingToShowText.IsEmpty() ? DropText : FText::Format(LOCTEXT("NothingToShowPattern", "{0}\n\n{1}"), NothingToShowText, DropText);}#if ECB_FEA_ASSET_DRAG_DROPbool SExtAssetView::OnDropAndDropToAssetView(const FAssetViewDragAndDropExtender::FPayload& InPayLoad){	ECB_LOG(Display, TEXT("FAssetViewDragAndDropExtenderHelper::OnDrop"));	TSharedPtr<FDragDropOperation> DragDropOp = InPayLoad.DragDropOp;	if (DragDropOp->IsOfType<FExtAssetDragDropOp>() && InPayLoad.PackagePaths.Num() > 0)	{		TSharedPtr<FExtAssetDragDropOp> AssetDragDropOp = StaticCastSharedPtr<FExtAssetDragDropOp>(DragDropOp);		const FString DestPath = InPayLoad.PackagePaths[0].ToString();		OnAssetsOrPathsDragDroppedToContentBrowser(AssetDragDropOp->GetAssets(), AssetDragDropOp->GetAssetPaths(), DestPath);		return true;	}	return false;}TSharedRef<FExtender> SExtAssetView::OnExtendLevelViewportMenu(const TSharedRef<FUICommandList> CommandList, const TArray<AActor*> InActors){	TSharedRef<FExtender> Extender(new FExtender());	int32 NumSelected = GetNumSelectedAssets();	if (NumSelected > 0)	{		FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked<FLevelEditorModule>(TEXT("LevelEditor"));		TSharedRef<FUICommandList> LevelEditorCommandBindings = LevelEditor.GetGlobalLevelEditorActions();		Extender->AddMenuExtension("ActorControl", EExtensionHook::After, LevelEditorCommandBindings, FMenuExtensionDelegate::CreateLambda(			[this, NumSelected](FMenuBuilder& MenuBuilder) {			MenuBuilder.BeginSection("UAssetBrowser", LOCTEXT("UAssetBrowser", "UAsset Browser"));			//MenuBuilder.AddMenuEntry(FExtContentBrowserCommands::Get().ImportSelectedUAsset, NAME_None, FText::Format(LOCTEXT("ImportSelectedUAsset", "Import {0} UAsset Files"), FText::AsNumber(NumSelected)));			MenuBuilder.AddMenuEntry(				FText::Format(LOCTEXT("ImportAndPlaceSelectedUAsset", "Import and place {0} asset(s) here"), FText::AsNumber(NumSelected))				, LOCTEXT("ImportAndPlaceSelectedUAssetTooltip", "Import selected UAsset Files to Content Browser and place them to current level."),				FSlateIcon(),				FUIAction(					FExecuteAction::CreateSP(this, &SExtAssetView::ExecuteImportAndPlaceSelecteUAssetFiles),					FCanExecuteAction::CreateLambda([this]() {return GetNumSelectedAssets() > 0; })				)			);			MenuBuilder.EndSection();		}		));	}		return Extender;}void SExtAssetView::OnAssetsOrPathsDragDroppedToContentBrowser(const TArray<FExtAssetData>& AssetList, const TArray<FString>& AssetPaths, const FString& DestinationPath){	DragDropHandler::HandleDropOnAssetFolderOfContentBrowser(		SharedThis(this),		AssetList,		AssetPaths,		DestinationPath,		FText::FromString(FPaths::GetCleanFilename(DestinationPath)),		DragDropHandler::FExecuteDropToImport::CreateSP(this, &SExtAssetView::ExecuteDropToContentBrowserToImport),		DragDropHandler::FExecuteDropToImport::CreateSP(this, &SExtAssetView::ExecuteDropToContentBrowserToImportFlat)	);}void SExtAssetView::ExecuteImportAndPlaceSelecteUAssetFiles(){	TArray<FExtAssetData> SelectedAssets = GetSelectedAssets();	if (SelectedAssets.Num() > 0)	{		FUAssetImportSetting ImportSetting = FUAssetImportSetting::GetSavedImportSetting();		ImportSetting.bPlaceImportedAssets = true;		FExtAssetImporter::ImportAssets(SelectedAssets, ImportSetting);	}}void SExtAssetView::ExecuteDropToContentBrowserToImport(TArray<FExtAssetData> AssetList, TArray<FString> AssetPaths, FString DestinationPath){	FExtAssetImporter::ImportAssets(AssetList, FUAssetImportSetting::GetSavedImportSetting());#if ECB_LEGACY	int32 NumItemsCopied = 0;	if (AssetList.Num() > 0)	{		TArray<UObject*> DroppedObjects;		ContentBrowserUtils::GetObjectsInAssetData(AssetList, DroppedObjects);		TArray<UObject*> NewObjects;		ObjectTools::DuplicateObjects(DroppedObjects, TEXT(""), DestinationPath, /*bOpenDialog=*/false, &NewObjects);		NumItemsCopied += NewObjects.Num();	}	if (AssetPaths.Num() > 0)	{		if (ContentBrowserUtils::CopyFolders(AssetPaths, DestinationPath))		{			NumItemsCopied += AssetPaths.Num();		}	}	// If any items were duplicated, report the success	if (NumItemsCopied > 0)	{		const FText Message = FText::Format(LOCTEXT("AssetItemsDroppedCopy", "{0} {0}|plural(one=item,other=items) copied"), NumItemsCopied);		const FVector2D& CursorPos = FSlateApplication::Get().GetCursorPos();		FSlateRect MessageAnchor(CursorPos.X, CursorPos.Y, CursorPos.X, CursorPos.Y);		ContentBrowserUtils::DisplayMessage(Message, MessageAnchor, SharedThis(this));	}#endif}void SExtAssetView::ExecuteDropToContentBrowserToImportFlat(TArray<FExtAssetData> AssetList, TArray<FString> AssetPaths, FString DestinationPath){	FExtAssetImporter::ImportAssetsToFolderPackagePath(AssetList, FUAssetImportSetting::GetFlatModeImportSetting(), 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);	}	OnFolderPathChanged.ExecuteIfBound(MovedFolders);#endif}#endif#if ECB_LEGACYbool SExtAssetView::HasSingleCollectionSource() const{	return ( SourcesData.Collections.Num() == 1 && SourcesData.PackagePaths.Num() == 0 );}void SExtAssetView::OnAssetsOrPathsDragDropped(const TArray<FAssetData>& AssetList, const TArray<FString>& AssetPaths, const FString& DestinationPath){	DragDropHandler::HandleDropOnAssetFolder(		SharedThis(this), 		AssetList, 		AssetPaths, 		DestinationPath, 		FText::FromString(FPaths::GetCleanFilename(DestinationPath)), 		DragDropHandler::FExecuteCopyOrMove::CreateSP(this, &SExtAssetView::ExecuteDropCopy),		DragDropHandler::FExecuteCopyOrMove::CreateSP(this, &SExtAssetView::ExecuteDropMove),		DragDropHandler::FExecuteCopyOrMove::CreateSP(this, &SExtAssetView::ExecuteDropAdvancedCopy)		);}void SExtAssetView::OnFilesDragDropped(const TArray<FString>& AssetList, const FString& DestinationPath){	FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");	AssetToolsModule.Get().ImportAssets( AssetList, DestinationPath );}void SExtAssetView::ExecuteDropCopy(TArray<FAssetData> AssetList, TArray<FString> AssetPaths, FString DestinationPath){	int32 NumItemsCopied = 0;	if (AssetList.Num() > 0)	{		TArray<UObject*> DroppedObjects;		ContentBrowserUtils::GetObjectsInAssetData(AssetList, DroppedObjects);		TArray<UObject*> NewObjects;		ObjectTools::DuplicateObjects(DroppedObjects, TEXT(""), DestinationPath, /*bOpenDialog=*/false, &NewObjects);		NumItemsCopied += NewObjects.Num();	}	if (AssetPaths.Num() > 0)	{		if (ContentBrowserUtils::CopyFolders(AssetPaths, DestinationPath))		{			NumItemsCopied += AssetPaths.Num();		}	}	// If any items were duplicated, report the success	if (NumItemsCopied > 0)	{		const FText Message = FText::Format(LOCTEXT("AssetItemsDroppedCopy", "{0} {0}|plural(one=item,other=items) copied"), NumItemsCopied);		const FVector2D& CursorPos = FSlateApplication::Get().GetCursorPos();		FSlateRect MessageAnchor(CursorPos.X, CursorPos.Y, CursorPos.X, CursorPos.Y);		ContentBrowserUtils::DisplayMessage(Message, MessageAnchor, SharedThis(this));	}}void SExtAssetView::ExecuteDropMove(TArray<FAssetData> AssetList, TArray<FString> AssetPaths, FString DestinationPath){	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);	}	OnFolderPathChanged.ExecuteIfBound(MovedFolders);}void SExtAssetView::ExecuteDropAdvancedCopy(TArray<FAssetData> AssetList, TArray<FString> AssetPaths, FString DestinationPath){	ContentBrowserUtils::BeginAdvancedCopyPackages(AssetList, AssetPaths, DestinationPath);}#endif#if ECB_FEA_SEARCHvoid SExtAssetView::SetUserSearching(bool bInSearching){	if(bUserSearching != bInSearching)	{		RequestSlowFullListRefresh();	}	bUserSearching = bInSearching;}#endifvoid SExtAssetView::HandleSettingChanged(FName PropertyName){	if ((PropertyName == GET_MEMBER_NAME_CHECKED(UExtContentBrowserSettings, DisplayFolders)) ||		(PropertyName == GET_MEMBER_NAME_CHECKED(UExtContentBrowserSettings, DisplayEmptyFolders)) ||		(PropertyName == "DisplayDevelopersFolder") ||		(PropertyName == "DisplayEngineFolder") ||		(PropertyName == NAME_None))	// @todo: Needed if PostEditChange was called manually, for now	{		RequestSlowFullListRefresh();	}}void SExtAssetView::OnAssetRegistryAssetGathered(const FExtAssetData& AssetData, int32 Left){	RecentlyAddedAssets.Add(AssetData);}void SExtAssetView::OnAssetRegistryRootPathAdded(const FString& Path){}void SExtAssetView::OnAssetRegistryRootPathRemoved(const FString& Path){	// Clear all	SetSourcesData(FSourcesData());}#if ECB_WIP_BREADCRUMBFText SExtAssetView::GetQuickJumpTerm() const{	return FText::FromString(QuickJumpData.JumpTerm);}EVisibility SExtAssetView::IsQuickJumpVisible() const{	return (QuickJumpData.JumpTerm.IsEmpty()) ? EVisibility::Collapsed : EVisibility::HitTestInvisible;}FSlateColor SExtAssetView::GetQuickJumpColor() const{	return FAppStyle::GetColor((QuickJumpData.bHasValidMatch) ? "InfoReporting.BackgroundColor" : "ErrorReporting.BackgroundColor");}void SExtAssetView::ResetQuickJump(){	QuickJumpData.JumpTerm.Empty();	QuickJumpData.bIsJumping = false;	QuickJumpData.bHasChangedSinceLastTick = false;	QuickJumpData.bHasValidMatch = false;}FReply SExtAssetView::HandleQuickJumpKeyDown(const TCHAR InCharacter, const bool bIsControlDown, const bool bIsAltDown, const bool bTestOnly){	// Check for special characters	if(bIsControlDown || bIsAltDown)	{		return FReply::Unhandled();	}	// Check for invalid characters	for(int InvalidCharIndex = 0; InvalidCharIndex < UE_ARRAY_COUNT(INVALID_OBJECTNAME_CHARACTERS) - 1; ++InvalidCharIndex)	{		if(InCharacter == INVALID_OBJECTNAME_CHARACTERS[InvalidCharIndex])		{			return FReply::Unhandled();		}	}	switch(InCharacter)	{	// Ignore some other special characters that we don't want to be entered into the buffer	case 0:		// Any non-character key press, e.g. f1-f12, Delete, Pause/Break, etc.				// These should be explicitly not handled so that their input bindings are handled higher up the chain.	case 8:		// Backspace	case 13:	// Enter	case 27:	// Esc		return FReply::Unhandled();	default:		break;	}	// Any other character!	if(!bTestOnly)	{		QuickJumpData.JumpTerm.AppendChar(InCharacter);		QuickJumpData.bHasChangedSinceLastTick = true;	}	return FReply::Handled();}bool SExtAssetView::PerformQuickJump(const bool bWasJumping){	auto GetAssetViewItemName = [](const TSharedPtr<FExtAssetViewItem> &Item) -> FString	{		switch(Item->GetType())		{		case EAssetItemType::Normal:			{				const TSharedPtr<FExtAssetViewAsset>& ItemAsAsset = StaticCastSharedPtr<FExtAssetViewAsset>(Item);				return ItemAsAsset->Data.AssetName.ToString();			}		case EAssetItemType::Folder:			{				const TSharedPtr<FAssetViewFolder>& ItemAsFolder = StaticCastSharedPtr<FAssetViewFolder>(Item);				return ItemAsFolder->FolderName.ToString();			}		default:			return FString();		}	};	auto JumpToNextMatch = [this, &GetAssetViewItemName](const int StartIndex, const int EndIndex) -> bool	{		check(StartIndex >= 0);		check(EndIndex <= FilteredAssetItems.Num());		for(int NewSelectedItemIndex = StartIndex; NewSelectedItemIndex < EndIndex; ++NewSelectedItemIndex)		{			TSharedPtr<FExtAssetViewItem>& NewSelectedItem = FilteredAssetItems[NewSelectedItemIndex];			const FString NewSelectedItemName = GetAssetViewItemName(NewSelectedItem);			if(NewSelectedItemName.StartsWith(QuickJumpData.JumpTerm, ESearchCase::IgnoreCase))			{				SetSelection(NewSelectedItem);				RequestScrollIntoView(NewSelectedItem);				return true;			}		}		return false;	};	TArray<TSharedPtr<FExtAssetViewItem>> SelectedItems = GetSelectedItems();	TSharedPtr<FExtAssetViewItem> SelectedItem = (SelectedItems.Num()) ? SelectedItems[0] : nullptr;	// If we have a selection, and we were already jumping, first check to see whether 	// the current selection still matches the quick-jump term; if it does, we do nothing	if(bWasJumping && SelectedItem.IsValid())	{		const FString SelectedItemName = GetAssetViewItemName(SelectedItem);		if(SelectedItemName.StartsWith(QuickJumpData.JumpTerm, ESearchCase::IgnoreCase))		{			return true;		}	}	// We need to move on to the next match in FilteredAssetItems that starts with the given quick-jump term	const int SelectedItemIndex = (SelectedItem.IsValid()) ? FilteredAssetItems.Find(SelectedItem) : INDEX_NONE;	const int StartIndex = (SelectedItemIndex == INDEX_NONE) ? 0 : SelectedItemIndex + 1;		bool ValidMatch = JumpToNextMatch(StartIndex, FilteredAssetItems.Num());	if(!ValidMatch && StartIndex > 0)	{		// If we didn't find a match, we need to loop around and look again from the start (assuming we weren't already)		return JumpToNextMatch(0, StartIndex);	}	return ValidMatch;}#endifvoid SExtAssetView::ToggleMajorAssetTypeColumns(){	bShowMajorAssetTypeColumnsInColumnView = !bShowMajorAssetTypeColumnsInColumnView;	ColumnView->GetHeaderRow()->RefreshColumns();	ColumnView->RebuildList();}bool SExtAssetView::IsShowingMajorAssetTypeColumns() const{	return bShowMajorAssetTypeColumnsInColumnView;}void SExtAssetView::FillToggleColumnsMenu(FMenuBuilder& MenuBuilder){	// Column view may not be valid if we toggled off columns view while the columns menu was open	if(ColumnView.IsValid())	{		const TIndirectArray<SHeaderRow::FColumn> Columns = ColumnView->GetHeaderRow()->GetColumns();		for (int32 ColumnIndex = 0; ColumnIndex < Columns.Num(); ++ColumnIndex)		{			const FString ColumnName = Columns[ColumnIndex].ColumnId.ToString();			MenuBuilder.AddMenuEntry(				Columns[ColumnIndex].DefaultText,				LOCTEXT("ShowHideColumnTooltip", "Show or hide column"),				FSlateIcon(),				FUIAction(					FExecuteAction::CreateSP(this, &SExtAssetView::ToggleColumn, ColumnName),					FCanExecuteAction::CreateSP(this, &SExtAssetView::CanToggleColumn, ColumnName),					FIsActionChecked::CreateSP(this, &SExtAssetView::IsColumnVisible, ColumnName),					EUIActionRepeatMode::RepeatEnabled				),				NAME_None,				EUserInterfaceActionType::Check			);		}	}}void SExtAssetView::ResetColumns(){	HiddenColumnNames.Empty();	NumVisibleColumns = ColumnView->GetHeaderRow()->GetColumns().Num();	ColumnView->GetHeaderRow()->RefreshColumns();	ColumnView->RebuildList();}void SExtAssetView::ExportColumns(){	IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();	const void* ParentWindowWindowHandle = FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr);	const FText Title = LOCTEXT("ExportToCSV", "Export columns as CSV...");	const FString FileTypes = TEXT("Data Table CSV (*.csv)|*.csv");	TArray<FString> OutFilenames;	DesktopPlatform->SaveFileDialog(		ParentWindowWindowHandle,		Title.ToString(),		TEXT(""),		TEXT("Report.csv"),		FileTypes,		EFileDialogFlags::None,		OutFilenames	);	if (OutFilenames.Num() > 0)	{		const TIndirectArray<SHeaderRow::FColumn>& Columns = ColumnView->GetHeaderRow()->GetColumns();		TArray<FName> ColumnNames;		for (const SHeaderRow::FColumn& Column : Columns)		{			ColumnNames.Add(Column.ColumnId);		}		FString SaveString;		SortManager.ExportColumnsToCSV(FilteredAssetItems, ColumnNames, CustomColumns, SaveString);		FFileHelper::SaveStringToFile(SaveString, *OutFilenames[0]);	}}void SExtAssetView::ToggleColumn(const FString ColumnName){	SetColumnVisibility(ColumnName, HiddenColumnNames.Contains(ColumnName));}void SExtAssetView::SetColumnVisibility(const FString ColumnName, const bool bShow){	if (!bShow)	{		--NumVisibleColumns;		HiddenColumnNames.Add(ColumnName);	}	else	{		++NumVisibleColumns;		check(HiddenColumnNames.Contains(ColumnName));		HiddenColumnNames.Remove(ColumnName);	}	ColumnView->GetHeaderRow()->RefreshColumns();	ColumnView->RebuildList();}bool SExtAssetView::CanToggleColumn(const FString ColumnName) const{	return (HiddenColumnNames.Contains(ColumnName) || NumVisibleColumns > 1);}bool SExtAssetView::IsColumnVisible(const FString ColumnName) const{	const FName ColumnNameToCheck(*ColumnName);	const bool bAssetTypeSpecificColumns = ColumnNameToCheck != SortManager.NameColumnId		&& ColumnNameToCheck != SortManager.ClassColumnId		&& ColumnNameToCheck != SortManager.PathColumnId;	const bool bHideMajorAssetTypeColumns = !bShowMajorAssetTypeColumnsInColumnView && bAssetTypeSpecificColumns;	return !bHideMajorAssetTypeColumns && !HiddenColumnNames.Contains(ColumnName);}bool SExtAssetView::ShouldColumnGenerateWidget(const FString ColumnName) const{	return IsColumnVisible(ColumnName);// !HiddenColumnNames.Contains(ColumnName);}TSharedRef<SWidget> SExtAssetView::CreateRowHeaderMenuContent(const FString ColumnName){	FMenuBuilder MenuBuilder(true, NULL);	MenuBuilder.AddMenuEntry(		LOCTEXT("HideColumn", "Hide Column"),		LOCTEXT("HideColumnToolTip", "Hides this column."),		FSlateIcon(),		FUIAction(FExecuteAction::CreateSP(this, &SExtAssetView::SetColumnVisibility, ColumnName, false), FCanExecuteAction::CreateSP(this, &SExtAssetView::CanToggleColumn, ColumnName)),		NAME_None,		EUserInterfaceActionType::Button);	return MenuBuilder.MakeWidget();}#undef LOCTEXT_NAMESPACE
 |