SWwiseBrowser.cpp 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516
  1. /*******************************************************************************
  2. The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
  3. Technology released in source code form as part of the game integration package.
  4. The content of this file may not be used without valid licenses to the
  5. AUDIOKINETIC Wwise Technology.
  6. Note that the use of the game engine is subject to the Unreal(R) Engine End User
  7. License Agreement at https://www.unrealengine.com/en-US/eula/unreal
  8. License Usage
  9. Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
  10. this file in accordance with the end user license agreement provided with the
  11. software or, alternatively, in accordance with the terms contained
  12. in a written agreement between you and Audiokinetic Inc.
  13. Copyright (c) 2023 Audiokinetic Inc.
  14. *******************************************************************************/
  15. /*------------------------------------------------------------------------------------
  16. SWwiseBrowser.cpp
  17. ------------------------------------------------------------------------------------*/
  18. #include "SWwiseBrowser.h"
  19. #include "IDesktopPlatform.h"
  20. #include "ISettingsModule.h"
  21. #include "Async/Async.h"
  22. #include "Developer/DesktopPlatform/Public/DesktopPlatformModule.h"
  23. #include "DragAndDrop/AssetDragDropOp.h"
  24. #include "Editor/UnrealEd/Public/EditorDirectories.h"
  25. #include "Framework/Application/SlateApplication.h"
  26. #include "Framework/Commands/GenericCommands.h"
  27. #include "Framework/MultiBox/MultiBoxBuilder.h"
  28. #include "Misc/MessageDialog.h"
  29. #include "Misc/ScopedSlowTask.h"
  30. #include "Widgets/Images/SLayeredImage.h"
  31. #include "Widgets/Input/SHyperlink.h"
  32. #include "Widgets/Input/SComboButton.h"
  33. #include "Widgets/Input/SSearchBox.h"
  34. #include "Widgets/Input/SComboButton.h"
  35. #include "Widgets/Layout/SSeparator.h"
  36. #include "Widgets/Layout/SSpacer.h"
  37. #include "AkAudioBankGenerationHelpers.h"
  38. #include "AkAudioModule.h"
  39. #include "AkAudioStyle.h"
  40. #include "AkSettings.h"
  41. #include "AkSettingsPerUser.h"
  42. #include "AkUnrealHelper.h"
  43. #include "IAudiokineticTools.h"
  44. #include "AssetManagement/AkAssetDatabase.h"
  45. #include "Wwise/WwiseProjectDatabase.h"
  46. #include "Wwise/Metadata/WwiseMetadataProjectInfo.h"
  47. #include "WwiseBrowser/WwiseAssetDragDropOp.h"
  48. #include "WwiseBrowser/WwiseBrowserHelpers.h"
  49. #include "WwiseBrowser/WwiseBrowserViewCommands.h"
  50. #include "SWwiseBrowserTreeView.h"
  51. #include "SoundBankStatusColumn.h"
  52. #include "WwiseBrowserTreeColumn.h"
  53. #include "WwiseStatusColumn.h"
  54. #include "WwiseUEAssetStatusColumn.h"
  55. #include "DataSource/WwiseBrowserDataSource.h"
  56. #include "AkWaapiUMG/Components/AkBoolPropertyToControlCustomization.h"
  57. #define LOCTEXT_NAMESPACE "AkAudio"
  58. const FName SWwiseBrowser::WwiseBrowserTabName = "WwiseBrowser";
  59. namespace SWwiseBrowser_Helper
  60. {
  61. const FName DirectoryWatcherModuleName = "DirectoryWatcher";
  62. }
  63. namespace FilterMenuTitles
  64. {
  65. FText GetMenuTitle(ESoundBankStatusFilter Filter)
  66. {
  67. switch (Filter)
  68. {
  69. case NewInWwise:
  70. return LOCTEXT("NewInWwise", "New In Wwise");
  71. case DeletedInWwise:
  72. return LOCTEXT("DeletedInWwise", "Deleted In Wwise");
  73. case RenamedInWwise:
  74. return LOCTEXT("RenamedInWwise", "Renamed In Wwise");
  75. case NotInWwise:
  76. return LOCTEXT("NotInWwiseOrSoundBank", "Not In Wwise or SoundBank");
  77. case MovedInWwise:
  78. return LOCTEXT("MovedInWwise", "Moved In Wwise");
  79. case UpToDate:
  80. return LOCTEXT("SoundBankUpToDate", "SoundBank Up To Date");
  81. }
  82. return FText();
  83. }
  84. FText GetMenuTooltip(ESoundBankStatusFilter Filter)
  85. {
  86. switch (Filter)
  87. {
  88. case NewInWwise:
  89. return LOCTEXT("NewInWwise_Tooltip", "Filter the New in Wwise Items.");
  90. case DeletedInWwise:
  91. return LOCTEXT("DeletedInWwise_Tooltip", "Filter the Deleted Items in Wwise.");
  92. case RenamedInWwise:
  93. return LOCTEXT("RenamedInWwise_Tooltip", "Filter the Renamed Items in Wwise.");
  94. case NotInWwise:
  95. return LOCTEXT("NotInWwiseOrSoundBank_Tooltip", "Filter the Items not in Wwise or in SoundBank.");
  96. case MovedInWwise:
  97. return LOCTEXT("MovedInWwise_Tooltip", "Filter the Items in different locations in Wwise and in SoundBank.");
  98. case UpToDate:
  99. return LOCTEXT("SoundBankUpToDate_Tooltip", "Filter the Up to date Wwise Items.");
  100. }
  101. return FText();
  102. }
  103. FText GetMenuTitle(EUAssetStatusFilter Filter)
  104. {
  105. switch (Filter)
  106. {
  107. case UAssetMissing:
  108. return LOCTEXT("UAssetMissing", "UAsset Missing");
  109. case NotInSoundBankOrUnreal:
  110. return LOCTEXT("NotInSoundBankOrUnreal", "Not in SoundBank or Unreal");
  111. case UAssetOrphaned:
  112. return LOCTEXT("UAssetOrphaned", "UAsset Orphaned");
  113. case MultipleUAssets:
  114. return LOCTEXT("UAssetMultiple", "Multiple UAssets");
  115. case RenamedInSoundBank:
  116. return LOCTEXT("RenamedInSoundBank", "Renamed In SoundBank");
  117. case UAssetUpToDate:
  118. return LOCTEXT("UAssetUpToDate", "UAsset Up to Date");
  119. }
  120. return FText();
  121. }
  122. FText GetMenuTooltip(EUAssetStatusFilter Filter)
  123. {
  124. switch (Filter)
  125. {
  126. case UAssetMissing:
  127. return LOCTEXT("UAssetMissing_Tooltip", "Filter the Missing UAssets.");
  128. case NotInSoundBankOrUnreal:
  129. return LOCTEXT("NotInSoundBankOrUnreal_Tooltip", "Filter the UAssets not in SoundBanks or Unreal.");
  130. case UAssetOrphaned:
  131. return LOCTEXT("UAssetOrphaned_Tooltip", "Filter UAssets with no counterpart in Wwise.");
  132. case MultipleUAssets:
  133. return LOCTEXT("UAssetMultiple_Tooltip", "Filter Wwise items with multiple UAssets.");
  134. case RenamedInSoundBank:
  135. return LOCTEXT("UAssetOrphaned_Tooltip", "Filter UAssets with different names in the SoundBank.");
  136. case UAssetUpToDate:
  137. return LOCTEXT("UAssetUpToDate_Tooltip", "Filter UAssets that are up to date with Wwise.");
  138. }
  139. return FText();
  140. }
  141. FText GetMenuTitle(EWwiseTypeFilter Filter)
  142. {
  143. switch (Filter)
  144. {
  145. case AcousticTexture:
  146. return LOCTEXT("AcousticTexture", "Acoustic Texture");
  147. case Effects:
  148. return LOCTEXT("Effects", "Effects");
  149. case Events:
  150. return LOCTEXT("Events", "Events");
  151. case GameParameters:
  152. return LOCTEXT("GameParameters", "GameParameters");
  153. case MasterMixerHierarchy:
  154. return LOCTEXT("MasterMixerHierarchy", "Master Mixer Hierarchy (Bus)");
  155. case State:
  156. return LOCTEXT("States", "States");
  157. case Switch:
  158. return LOCTEXT("Switches", "Switches");
  159. case Trigger:
  160. return LOCTEXT("Triggers", "Triggers");
  161. }
  162. return FText();
  163. }
  164. FText GetMenuTooltip(EWwiseTypeFilter Filter)
  165. {
  166. switch (Filter)
  167. {
  168. case AcousticTexture:
  169. return LOCTEXT("AcousticTexture_Tooltip", "Filter Wwise Acoustic Texture Types.");
  170. case Effects:
  171. return LOCTEXT("Effects_Tooltip", "Filter Wwise Effect Types.");
  172. case Events:
  173. return LOCTEXT("Events_Tooltip", "Filter Wwise Event Types.");
  174. case GameParameters:
  175. return LOCTEXT("GameParameters_Tooltip", "Filter Wwise Game Parameter Types.");
  176. case MasterMixerHierarchy:
  177. return LOCTEXT("MasterMixerHierarchy_Tooltip", "Filter Wwise Bus Types.");
  178. case State:
  179. return LOCTEXT("State_Tooltip", "Filter Wwise State Types.");
  180. case Switch:
  181. return LOCTEXT("Switch_Tooltip", "Filter Wwise Switch Types.");
  182. case Trigger:
  183. return LOCTEXT("Trigger_Tooltip", "Filter Wwise Trigger Types.");
  184. }
  185. return FText();
  186. }
  187. }
  188. SWwiseBrowser::SWwiseBrowser(): CommandList(MakeShared<FUICommandList>())
  189. {
  190. AllowTreeViewDelegates = true;
  191. DataSource = MakeUnique<FWwiseBrowserDataSource>();
  192. Transport = MakeUnique<WaapiPlaybackTransport>();
  193. DataSource->WwiseBrowserDataSourceRefreshed.BindLambda([this]()
  194. {
  195. AsyncTask(ENamedThreads::Type::GameThread, [this]
  196. {
  197. this->ConstructTree();
  198. });
  199. });
  200. DataSource->WwiseSelectionChange.BindRaw(this, &SWwiseBrowser::UpdateWaapiSelection);
  201. DataSource->WwiseExpansionChange.BindRaw(this, &SWwiseBrowser::ExpandItems);
  202. }
  203. void SWwiseBrowser::CreateWwiseBrowserCommands()
  204. {
  205. const FWwiseBrowserViewCommands& Commands = FWwiseBrowserViewCommands::Get();
  206. FUICommandList& ActionList = *CommandList;
  207. // Action to start playing an event from the Wwise Browser.
  208. ActionList.MapAction(
  209. Commands.RequestPlayWwiseItem,
  210. FExecuteAction::CreateSP(this, &SWwiseBrowser::HandlePlayWwiseItemCommandExecute),
  211. FCanExecuteAction::CreateSP(this, &SWwiseBrowser::HandlePlayOrStopWwiseItemCanExecute));
  212. // Action to stop all playing Wwise item (event).
  213. ActionList.MapAction(
  214. Commands.RequestStopAllWwiseItem,
  215. FExecuteAction::CreateSP(this, &SWwiseBrowser::HandleStopAllWwiseItemCommandExecute));
  216. // Action to explore an item (workunit) in the containing folder.
  217. ActionList.MapAction(
  218. Commands.RequestExploreWwiseItem,
  219. FExecuteAction::CreateSP(this, &SWwiseBrowser::HandleExploreWwiseItemCommandExecute),
  220. FCanExecuteAction::CreateSP(this, &SWwiseBrowser::HandleExploreWwiseItemCanExecute));
  221. // Action find an item in the Wwise Project Explorer
  222. ActionList.MapAction(
  223. Commands.RequestFindInProjectExplorerWwisetem,
  224. FExecuteAction::CreateSP(this, &SWwiseBrowser::HandleFindInProjectExplorerWwiseItemCommandExecute),
  225. FCanExecuteAction::CreateSP(this, &SWwiseBrowser::HandleFindInProjectExplorerWwiseItemCanExecute));
  226. // Action to find the Unreal asset in the Content Browser that maps to this item
  227. ActionList.MapAction(
  228. Commands.RequestFindInContentBrowser,
  229. FExecuteAction::CreateSP(this, &SWwiseBrowser::HandleFindInContentBrowserCommandExecute),
  230. FCanExecuteAction::CreateSP(this, &SWwiseBrowser::HandleFindInContentBrowserCanExecute));
  231. // Action to refresh the Browser
  232. ActionList.MapAction(
  233. Commands.RequestRefreshWwiseBrowser,
  234. FExecuteAction::CreateSP(this, &SWwiseBrowser::HandleRefreshWwiseBrowserCommandExecute));
  235. // Action for importing the selected items from the Wwise Browser.
  236. ActionList.MapAction(
  237. Commands.RequestImportWwiseItem,
  238. FExecuteAction::CreateSP(this, &SWwiseBrowser::HandleImportWwiseItemCommandExecute));
  239. }
  240. TSharedPtr<SWidget> SWwiseBrowser::MakeWwiseBrowserContextMenu()
  241. {
  242. const FWwiseBrowserViewCommands& Commands = FWwiseBrowserViewCommands::Get();
  243. // Build up the menu
  244. FMenuBuilder MenuBuilder(true, CommandList);
  245. {
  246. MenuBuilder.BeginSection("WwiseBrowserTransport", LOCTEXT("MenuHeader", "WwiseBrowser"));
  247. {
  248. MenuBuilder.AddMenuEntry(Commands.RequestPlayWwiseItem);
  249. MenuBuilder.AddMenuEntry(Commands.RequestStopAllWwiseItem);
  250. }
  251. MenuBuilder.EndSection();
  252. MenuBuilder.BeginSection("WwiseBrowserFindOptions", LOCTEXT("ExploreMenuHeader", "Explore"));
  253. {
  254. MenuBuilder.AddMenuEntry(Commands.RequestFindInProjectExplorerWwisetem);
  255. MenuBuilder.AddMenuEntry(Commands.RequestFindInContentBrowser);
  256. MenuBuilder.AddMenuEntry(Commands.RequestExploreWwiseItem);
  257. }
  258. MenuBuilder.EndSection();
  259. MenuBuilder.BeginSection("WwiseBrowserRefreshAll", LOCTEXT("RefreshHeader", "Refresh"));
  260. {
  261. MenuBuilder.AddMenuEntry(Commands.RequestRefreshWwiseBrowser);
  262. }
  263. MenuBuilder.EndSection();
  264. MenuBuilder.BeginSection("WwiseBrowserImport", LOCTEXT("ImportHeader", "Import"));
  265. {
  266. MenuBuilder.AddMenuEntry(Commands.RequestImportWwiseItem);
  267. }
  268. MenuBuilder.EndSection();
  269. }
  270. return MenuBuilder.MakeWidget();
  271. }
  272. void SWwiseBrowser::HandlePlayWwiseItemCommandExecute()
  273. {
  274. TArray<FWwiseTreeItemPtr> SelectedItems = TreeViewPtr->GetSelectedItems();
  275. int32 TransportID = -1;
  276. for (auto WwiseTreeItem : SelectedItems)
  277. {
  278. bool bPlaying = Transport->IsPlaying(WwiseTreeItem->ItemId);
  279. if (WwiseTreeItem->bWaapiRefExists)
  280. {
  281. TransportID = Transport->FindOrAdd(WwiseTreeItem->ItemId);
  282. }
  283. if(TransportID >= 0 && !bPlaying)
  284. {
  285. Transport->TogglePlay(TransportID);
  286. }
  287. if(bPlaying)
  288. {
  289. Transport->Remove(WwiseTreeItem->ItemId);
  290. }
  291. }
  292. }
  293. bool SWwiseBrowser::HandlePlayOrStopWwiseItemCanExecute()
  294. {
  295. if (IsWaapiAvailable() != EWwiseConnectionStatus::Connected)
  296. {
  297. return false;
  298. }
  299. TArray<FWwiseTreeItemPtr> SelectedItems = TreeViewPtr->GetSelectedItems();
  300. if (SelectedItems.Num() == 0)
  301. {
  302. return false;
  303. }
  304. for (auto SelectedItem: SelectedItems)
  305. {
  306. if (!SelectedItem->bWaapiRefExists)
  307. {
  308. return false;
  309. }
  310. if (SelectedItem->IsNotOfType({ EWwiseItemType::Event, EWwiseItemType::Sound, EWwiseItemType::BlendContainer, EWwiseItemType::SwitchContainer, EWwiseItemType::RandomSequenceContainer }))
  311. {
  312. return false;
  313. }
  314. }
  315. return true;
  316. }
  317. void SWwiseBrowser::HandleStopWwiseItemCommandExecute()
  318. {
  319. TArray<FWwiseTreeItemPtr> SelectedItems = TreeViewPtr->GetSelectedItems();
  320. int32 TransportID = -1;
  321. for (auto WwiseTreeItem : SelectedItems)
  322. {
  323. if (WwiseTreeItem->bWaapiRefExists)
  324. {
  325. TransportID = Transport->FindOrAdd(WwiseTreeItem->ItemId);
  326. }
  327. Transport->Stop(TransportID);
  328. }
  329. }
  330. void SWwiseBrowser::HandleStopAllWwiseItemCommandExecute()
  331. {
  332. Transport->StopAndDestroyAll();
  333. }
  334. void SWwiseBrowser::HandleExploreWwiseItemCommandExecute()
  335. {
  336. FWwiseTreeItemPtr SelectedItem = TreeViewPtr->GetSelectedItems()[0];
  337. FString WorkUnitPath = DataSource->GetItemWorkUnitPath(SelectedItem);
  338. FPlatformProcess::ExploreFolder(*WorkUnitPath);
  339. }
  340. bool SWwiseBrowser::HandleExploreWwiseItemCanExecute()
  341. {
  342. return TreeViewPtr->GetSelectedItems().Num() == 1 && IsWaapiAvailable() == EWwiseConnectionStatus::Connected;
  343. }
  344. void SWwiseBrowser::HandleFindInProjectExplorerWwiseItemCommandExecute()
  345. {
  346. TArray<FWwiseTreeItemPtr> SelectedItems = TreeViewPtr->GetSelectedItems();
  347. DataSource->SelectInWwiseProjectExplorer(SelectedItems);
  348. }
  349. bool SWwiseBrowser::HandleFindInProjectExplorerWwiseItemCanExecute()
  350. {
  351. return TreeViewPtr->GetSelectedItems().Num() >= 1 && IsWaapiAvailable() == EWwiseConnectionStatus::Connected;
  352. }
  353. bool SWwiseBrowser::HandleFindInContentBrowserCanExecute()
  354. {
  355. TArray<FWwiseTreeItemPtr> SelectedItems = TreeViewPtr->GetSelectedItems();
  356. for (const auto& Asset : SelectedItems)
  357. {
  358. if (Asset->IsOfType({ EWwiseItemType::Event,
  359. EWwiseItemType::Bus,
  360. EWwiseItemType::AuxBus,
  361. EWwiseItemType::AcousticTexture,
  362. EWwiseItemType::State,
  363. EWwiseItemType::Switch,
  364. EWwiseItemType::GameParameter,
  365. EWwiseItemType::Trigger,
  366. EWwiseItemType::EffectShareSet
  367. }))
  368. {
  369. return true;
  370. }
  371. }
  372. return false;
  373. }
  374. void SWwiseBrowser::HandleFindInContentBrowserCommandExecute()
  375. {
  376. TArray<FWwiseTreeItemPtr> SelectedItems = TreeViewPtr->GetSelectedItems();
  377. TArray<FAssetData> AssetsToSync;
  378. for (const auto& Asset : SelectedItems)
  379. {
  380. AssetsToSync.Append(Asset->Assets);
  381. }
  382. if (AssetsToSync.Num())
  383. {
  384. GEditor->SyncBrowserToObjects(AssetsToSync);
  385. }
  386. }
  387. void SWwiseBrowser::HandleRefreshWwiseBrowserCommandExecute()
  388. {
  389. DataSource->ConstructTree();
  390. }
  391. SWwiseBrowser::~SWwiseBrowser()
  392. {
  393. RootItems.Empty();
  394. SearchBoxFilter->OnChanged().RemoveAll(this);
  395. DataSource->WwiseSelectionChange.Unbind();
  396. DataSource->WwiseExpansionChange.Unbind();
  397. }
  398. void SWwiseBrowser::Construct(const FArguments& InArgs)
  399. {
  400. FGenericCommands::Register();
  401. FWwiseBrowserViewCommands::Register();
  402. CreateWwiseBrowserCommands();
  403. SearchBoxFilter = MakeShareable(new StringFilter(StringFilter::FItemToStringArray::CreateSP(this, &SWwiseBrowser::PopulateSearchStrings)));
  404. SearchBoxFilter->OnChanged().AddSP(this, &SWwiseBrowser::OnTextFilterUpdated);
  405. HeaderRowWidget =
  406. SNew(SHeaderRow)
  407. .CanSelectGeneratedColumn(true);
  408. #if UE_5_0_OR_LATER
  409. TSharedPtr<SLayeredImage> FilterImage = SNew(SLayeredImage)
  410. .Image(FAkAppStyle::Get().GetBrush("Icons.Filter"))
  411. .ColorAndOpacity(FSlateColor::UseForeground());
  412. #if UE_5_1_OR_LATER
  413. // Badge the filter icon if there are filters active
  414. FilterImage->AddLayer(TAttribute<const FSlateBrush*>(this, &SWwiseBrowser::GetFilterBadgeIcon));
  415. #endif
  416. #endif
  417. SetupColumns(*HeaderRowWidget);
  418. ChildSlot
  419. [
  420. SNew(SBorder)
  421. .Padding(4)
  422. .BorderImage(FAkAppStyle::Get().GetBrush("ToolPanel.GroupBorder"))
  423. [
  424. SNew(SOverlay)
  425. // Browser
  426. + SOverlay::Slot()
  427. .VAlign(VAlign_Fill)
  428. [
  429. SNew(SVerticalBox)
  430. // Search
  431. + SVerticalBox::Slot()
  432. .AutoHeight()
  433. .Padding(0, 1, 0, 3)
  434. [
  435. SNew(SHorizontalBox)
  436. + SHorizontalBox::Slot()
  437. .AutoWidth()
  438. [
  439. InArgs._SearchContent.Widget
  440. ]
  441. + SHorizontalBox::Slot()
  442. .AutoWidth()
  443. .HAlign(HAlign_Left)
  444. [
  445. SNew(SComboButton)
  446. #if UE_5_0_OR_LATER
  447. .ComboButtonStyle(FAkAppStyle::Get(), "SimpleComboButton")
  448. #else
  449. .ComboButtonStyle(FEditorStyle::Get(), "GenericFilters.ComboButtonStyle")
  450. .ForegroundColor(FLinearColor::White)
  451. #endif
  452. .ToolTipText(LOCTEXT("Browser_AddFilterToolTip", "Add filters to the Wwise Browser."))
  453. .OnGetMenuContent(this, &SWwiseBrowser::MakeAddFilterMenu)
  454. .ButtonContent()
  455. [
  456. SNew(SHorizontalBox)
  457. + SHorizontalBox::Slot()
  458. .AutoWidth()
  459. [
  460. #if UE_5_0_OR_LATER
  461. FilterImage.ToSharedRef()
  462. #else
  463. SNew(STextBlock)
  464. .TextStyle(FEditorStyle::Get(), "GenericFilters.TextStyle")
  465. .Font(FEditorStyle::Get().GetFontStyle("FontAwesome.9"))
  466. .Text(FText::FromString(FString(TEXT("\xf0b0"))) /*fa-filter*/)
  467. #endif
  468. ]
  469. ]
  470. ]
  471. + SHorizontalBox::Slot()
  472. .FillWidth(1.0f)
  473. [
  474. SNew(SSearchBox)
  475. .HintText(LOCTEXT("WwiseBrowserSearchTooltip", "Search Wwise Item"))
  476. .OnTextChanged(this, &SWwiseBrowser::OnSearchBoxChanged)
  477. .SelectAllTextWhenFocused(false)
  478. .DelayChangeNotificationsWhileTyping(true)
  479. ]
  480. + SHorizontalBox::Slot()
  481. .AutoWidth()
  482. [
  483. SNew(SBox)
  484. .Padding(FMargin(5.0f,0.0f,30.0f,0.0f))
  485. [
  486. SNew(SButton)
  487. .ToolTipText(LOCTEXT("WwiseBrowserRefresh", "Refresh the Wwise Browser"))
  488. .OnClicked(this, &SWwiseBrowser::OnRefreshClicked)
  489. [
  490. SNew(SImage)
  491. .Image(FAkAppStyle::Get().GetBrush("Icons.Refresh"))
  492. ]
  493. ]
  494. ]
  495. + SHorizontalBox::Slot()
  496. .AutoWidth()
  497. [
  498. SNew(SButton)
  499. .Text(LOCTEXT("AkBrowserGenerate", "Generate SoundBanks..."))
  500. .OnClicked(this, &SWwiseBrowser::OnGenerateSoundBanksClicked)
  501. ]
  502. ]
  503. // Separator
  504. + SVerticalBox::Slot()
  505. .AutoHeight()
  506. .Padding(0, 0, 0, 1)
  507. [
  508. SNew(SSeparator)
  509. .Visibility((InArgs._ShowSeparator) ? EVisibility::Visible : EVisibility::Collapsed)
  510. ]
  511. // Tree
  512. + SVerticalBox::Slot()
  513. .FillHeight(1.f)
  514. [
  515. SAssignNew(TreeViewPtr, SWwiseBrowserTreeView, StaticCastSharedRef<SWwiseBrowser>(AsShared()))
  516. .TreeItemsSource(&RootItems).Visibility(this, &SWwiseBrowser::IsWarningNotVisible)
  517. .OnGenerateRow(this, &SWwiseBrowser::GenerateRow)
  518. .ItemHeight(18)
  519. .SelectionMode(InArgs._SelectionMode)
  520. .OnSelectionChanged(this, &SWwiseBrowser::TreeSelectionChanged)
  521. .OnExpansionChanged(this, &SWwiseBrowser::TreeExpansionChanged)
  522. .OnGetChildren(this, &SWwiseBrowser::GetChildrenForTree)
  523. .OnContextMenuOpening(this, &SWwiseBrowser::MakeWwiseBrowserContextMenu)
  524. .ClearSelectionOnClick(false)
  525. .HeaderRow(HeaderRowWidget)
  526. .OnMouseButtonDoubleClick(this, &SWwiseBrowser::OnTreeItemDoubleClicked)
  527. ]
  528. //Footer
  529. + SVerticalBox::Slot()
  530. .AutoHeight()
  531. [
  532. SNew(SVerticalBox)
  533. + SVerticalBox::Slot()
  534. .VAlign(VAlign_Bottom)
  535. [
  536. SNew(SVerticalBox)
  537. + SVerticalBox::Slot()
  538. .AutoHeight()
  539. [
  540. SNew(SBorder)
  541. .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
  542. .Padding(1.0f)
  543. [
  544. SNew(SHorizontalBox)
  545. + SHorizontalBox::Slot()
  546. [
  547. SNew(STextBlock)
  548. .Text(this, &SWwiseBrowser::GetConnectionStatusText)
  549. .Visibility(this, &SWwiseBrowser::IsWarningNotVisible)
  550. ]
  551. + SHorizontalBox::Slot()
  552. [
  553. SNew(STextBlock)
  554. .Text(this, &SWwiseBrowser::GetSoundBanksLocationText)
  555. ]
  556. ]
  557. ]
  558. ]
  559. ]
  560. ]
  561. // Empty Browser
  562. + SOverlay::Slot()
  563. .VAlign(VAlign_Center)
  564. .HAlign(HAlign_Center)
  565. [
  566. SNew(SVerticalBox)
  567. + SVerticalBox::Slot()
  568. .VAlign(VAlign_Center)
  569. .HAlign(HAlign_Center)
  570. .AutoHeight()
  571. [
  572. SNew(STextBlock)
  573. .Visibility(this, &SWwiseBrowser::IsWarningVisible)
  574. .AutoWrapText(true)
  575. .Justification(ETextJustify::Center)
  576. .Text(this, &SWwiseBrowser::GetWarningText)
  577. ]
  578. + SVerticalBox::Slot()
  579. .VAlign(VAlign_Center)
  580. .HAlign(HAlign_Center)
  581. .AutoHeight()
  582. [
  583. SNew(SButton)
  584. .Text(LOCTEXT("AkBrowserOpenSettings", "Open Wwise Integration Settings"))
  585. .Visibility(this, &SWwiseBrowser::IsWarningVisible)
  586. .OnClicked(this, &SWwiseBrowser::OnOpenSettingsClicked)
  587. ]
  588. + SVerticalBox::Slot()
  589. .VAlign(VAlign_Center)
  590. .HAlign(HAlign_Center)
  591. .AutoHeight()
  592. ]
  593. ]
  594. ];
  595. InitialParse();
  596. }
  597. TSharedRef<SWidget> SWwiseBrowser::MakeAddFilterMenu()
  598. {
  599. FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/false, nullptr);
  600. MenuBuilder.BeginSection("SoundBankStatusFilters", LOCTEXT("SoundBankStatusFilters", "SoundBank Status Filters"));
  601. {
  602. for (int i = 0; i < NumberOfSoundBankStatus; i++)
  603. {
  604. ESoundBankStatusFilter Status = (ESoundBankStatusFilter)i;
  605. MenuBuilder.AddMenuEntry(
  606. FilterMenuTitles::GetMenuTitle(Status),
  607. FilterMenuTitles::GetMenuTooltip(Status),
  608. FSlateIcon(),
  609. FUIAction(FExecuteAction::CreateSP(this, &SWwiseBrowser::SoundBankFilterExecute, Status),
  610. FCanExecuteAction::CreateLambda([] { return true; }),
  611. FIsActionChecked::CreateSP(this, &SWwiseBrowser::SoundBankFilterIsChecked, Status)),
  612. NAME_None,
  613. EUserInterfaceActionType::ToggleButton
  614. );
  615. }
  616. MenuBuilder.AddMenuEntry(
  617. LOCTEXT("SoundBankNotUpToDate", "SoundBank Not Up to Date"),
  618. LOCTEXT("SoundBankNotUpToDate_Tooltip", "Activate all filters of this section that is not \"SoundBank Up to Date\""),
  619. FSlateIcon(),
  620. FUIAction(FExecuteAction::CreateSP(this, &SWwiseBrowser::SoundBankNotUpToDateExecute)),
  621. NAME_None,
  622. EUserInterfaceActionType::Button
  623. );
  624. }
  625. MenuBuilder.EndSection();
  626. MenuBuilder.BeginSection("UAssetStatusFilters", LOCTEXT("UAssetStatusFilters", "UAsset Status Filters"));
  627. {
  628. for(int i = 0; i < NumberOfUAssetStatus; i++)
  629. {
  630. EUAssetStatusFilter Status = (EUAssetStatusFilter)i;
  631. MenuBuilder.AddMenuEntry(
  632. FilterMenuTitles::GetMenuTitle(Status),
  633. FilterMenuTitles::GetMenuTooltip(Status),
  634. FSlateIcon(),
  635. FUIAction(FExecuteAction::CreateSP(this, &SWwiseBrowser::UAssetFilterExecute, Status),
  636. FCanExecuteAction::CreateLambda([] { return true; }),
  637. FIsActionChecked::CreateSP(this, &SWwiseBrowser::UAssetFilterIsChecked, Status)),
  638. NAME_None,
  639. EUserInterfaceActionType::ToggleButton
  640. );
  641. }
  642. MenuBuilder.AddMenuEntry(
  643. LOCTEXT("UAssetNotUpToDate", "UAsset Not Up to Date"),
  644. LOCTEXT("UAssetNotUpToDate_Tooltip", "Activate all filters of this section that is not \"UAsset Up to Date\""),
  645. FSlateIcon(),
  646. FUIAction(FExecuteAction::CreateSP(this, &SWwiseBrowser::UAssetNotUpToDateExecute)),
  647. NAME_None,
  648. EUserInterfaceActionType::Button
  649. );
  650. }
  651. MenuBuilder.EndSection();
  652. MenuBuilder.BeginSection("TypeFilters", LOCTEXT("TypeFilters", "Type Filters"));
  653. {
  654. for (int i = 0; i < NumberOfWwiseTypes; i++)
  655. {
  656. EWwiseTypeFilter Type = (EWwiseTypeFilter)i;
  657. MenuBuilder.AddMenuEntry(
  658. FilterMenuTitles::GetMenuTitle(Type),
  659. FilterMenuTitles::GetMenuTooltip(Type),
  660. FSlateIcon(),
  661. FUIAction(FExecuteAction::CreateSP(this, &SWwiseBrowser::WwiseTypeFilterExecute, Type),
  662. FCanExecuteAction::CreateLambda([] { return true; }),
  663. FIsActionChecked::CreateSP(this, &SWwiseBrowser::WwiseTypeFilterIsChecked, Type)),
  664. NAME_None,
  665. EUserInterfaceActionType::ToggleButton
  666. );
  667. }
  668. }
  669. MenuBuilder.EndSection();
  670. MenuBuilder.BeginSection("RemoveFilters", LOCTEXT("RemoveFilters", "Remove Filters"));
  671. {
  672. MenuBuilder.AddMenuEntry(
  673. LOCTEXT("Remove_Title", "Remove All Filters"),
  674. LOCTEXT("Remove_Tooltip", "Removes all selected filters."),
  675. FSlateIcon(),
  676. FUIAction(FExecuteAction::CreateSP(this, &SWwiseBrowser::RemoveFiltersExecute),
  677. FCanExecuteAction::CreateLambda([] { return true; })),
  678. NAME_None,
  679. EUserInterfaceActionType::Button
  680. );
  681. }
  682. return MenuBuilder.MakeWidget();
  683. }
  684. const FSlateBrush* SWwiseBrowser::GetFilterBadgeIcon() const
  685. {
  686. if (!SoundBankStatusFilter.AreFiltersOff())
  687. {
  688. return FAkAppStyle::Get().GetBrush("Icons.BadgeModified");
  689. }
  690. if (!UAssetStatusFilter.AreFiltersOff())
  691. {
  692. return FAkAppStyle::Get().GetBrush("Icons.BadgeModified");
  693. }
  694. if (!WwiseTypeFilter.AreFiltersOff())
  695. {
  696. return FAkAppStyle::Get().GetBrush("Icons.BadgeModified");
  697. }
  698. return nullptr;
  699. }
  700. void SWwiseBrowser::SoundBankFilterExecute(ESoundBankStatusFilter Filter)
  701. {
  702. SoundBankStatusFilter.bFilters[Filter] = !SoundBankStatusFilter.bFilters[Filter];
  703. OnFilterUpdated();
  704. }
  705. bool SWwiseBrowser::SoundBankFilterIsChecked(ESoundBankStatusFilter Filter)
  706. {
  707. return SoundBankStatusFilter.bFilters[Filter];
  708. }
  709. void SWwiseBrowser::UAssetFilterExecute(EUAssetStatusFilter Filter)
  710. {
  711. UAssetStatusFilter.bFilters[Filter] = !UAssetStatusFilter.bFilters[Filter];
  712. OnFilterUpdated();
  713. }
  714. bool SWwiseBrowser::UAssetFilterIsChecked(EUAssetStatusFilter Filter)
  715. {
  716. return UAssetStatusFilter.bFilters[Filter];
  717. }
  718. void SWwiseBrowser::WwiseTypeFilterExecute(EWwiseTypeFilter Filter)
  719. {
  720. WwiseTypeFilter.bFilters[Filter] = !WwiseTypeFilter.bFilters[Filter];
  721. OnFilterUpdated();
  722. }
  723. bool SWwiseBrowser::WwiseTypeFilterIsChecked(EWwiseTypeFilter Filter)
  724. {
  725. return WwiseTypeFilter.bFilters[Filter];
  726. }
  727. void SWwiseBrowser::RemoveFiltersExecute()
  728. {
  729. bool bShouldUpdate = false;
  730. for (auto& bFilter : WwiseTypeFilter.bFilters)
  731. {
  732. if(bFilter)
  733. {
  734. bShouldUpdate = true;
  735. bFilter = false;
  736. }
  737. }
  738. for(auto& bFilter : SoundBankStatusFilter.bFilters)
  739. {
  740. if (bFilter)
  741. {
  742. bShouldUpdate = true;
  743. bFilter = false;
  744. }
  745. }
  746. for(auto& bFilter : UAssetStatusFilter.bFilters)
  747. {
  748. if (bFilter)
  749. {
  750. bShouldUpdate = true;
  751. bFilter = false;
  752. }
  753. }
  754. if(bShouldUpdate)
  755. {
  756. OnFilterUpdated();
  757. }
  758. }
  759. void SWwiseBrowser::SoundBankNotUpToDateExecute()
  760. {
  761. bool bShouldUpdate = false;
  762. for (int i = 0; i < ESoundBankStatusFilter::UpToDate; i++)
  763. {
  764. if (!SoundBankStatusFilter.bFilters[i])
  765. {
  766. SoundBankStatusFilter.bFilters[i] = true;
  767. bShouldUpdate = true;
  768. }
  769. }
  770. if (SoundBankStatusFilter.bFilters[ESoundBankStatusFilter::UpToDate])
  771. {
  772. SoundBankStatusFilter.bFilters[ESoundBankStatusFilter::UpToDate] = false;
  773. bShouldUpdate = true;
  774. }
  775. if (bShouldUpdate)
  776. {
  777. OnFilterUpdated();
  778. }
  779. }
  780. void SWwiseBrowser::UAssetNotUpToDateExecute()
  781. {
  782. bool bShouldUpdate = false;
  783. for (int i = 0; i < EUAssetStatusFilter::UAssetUpToDate; i++)
  784. {
  785. if (!UAssetStatusFilter.bFilters[i])
  786. {
  787. UAssetStatusFilter.bFilters[i] = true;
  788. bShouldUpdate = true;
  789. }
  790. }
  791. if (UAssetStatusFilter.bFilters[EUAssetStatusFilter::UAssetUpToDate])
  792. {
  793. UAssetStatusFilter.bFilters[EUAssetStatusFilter::UAssetUpToDate] = false;
  794. bShouldUpdate = true;
  795. }
  796. if (bShouldUpdate)
  797. {
  798. OnFilterUpdated();
  799. }
  800. }
  801. void SWwiseBrowser::ForceRefresh()
  802. {
  803. if(!GetProjectName().IsEmpty())
  804. {
  805. DataSource->ConstructTree();
  806. }
  807. }
  808. void SWwiseBrowser::InitialParse()
  809. {
  810. if(!GetProjectName().IsEmpty())
  811. {
  812. DataSource->ConstructTree();
  813. TreeViewPtr->RequestTreeRefresh();
  814. ExpandFirstLevel();
  815. }
  816. }
  817. FText SWwiseBrowser::GetProjectName() const
  818. {
  819. return DataSource->GetProjectName();
  820. }
  821. FText SWwiseBrowser::GetConnectedWwiseProjectName() const
  822. {
  823. return DataSource->GetConnectedWwiseProjectName();
  824. }
  825. EWwiseConnectionStatus SWwiseBrowser::IsWaapiAvailable() const
  826. {
  827. return DataSource->GetWaapiConnectionStatus();
  828. }
  829. EVisibility SWwiseBrowser::IsWarningVisible() const
  830. {
  831. // Also need to check if WAAPI available
  832. return GetProjectName().IsEmpty() ? EVisibility::Visible : EVisibility::Hidden;
  833. }
  834. EVisibility SWwiseBrowser::IsWarningNotVisible() const
  835. {
  836. return GetProjectName().IsEmpty() ? EVisibility::Hidden : EVisibility::Visible;
  837. }
  838. EVisibility SWwiseBrowser::IsItemPlaying(FGuid ItemId) const
  839. {
  840. return Transport->IsPlaying(ItemId) ? EVisibility::Visible : EVisibility::Hidden;
  841. }
  842. FText SWwiseBrowser::GetWarningText() const
  843. {
  844. if (auto AkSettings = GetMutableDefault<UAkSettings>())
  845. {
  846. if (AkSettings->GeneratedSoundBanksFolder.Path.IsEmpty())
  847. {
  848. const FText WarningText = LOCTEXT("BrowserSoundBanksFolderEmpty", "Generated SoundBanks Folder in Wwise Integration settings is empty.\nThis folder should match the \"Root Output Path\" in the Wwise Project's SoundBanks settings.");
  849. return WarningText;
  850. }
  851. }
  852. FString soundBankDirectory = AkUnrealHelper::GetSoundBankDirectory();
  853. const FText WarningText = FText::FormatOrdered(LOCTEXT("BrowserMissingSoundBanks", "SoundBank metadata was not found at path specified by \"Generated SoundBanks Folder\" setting: {0}.\nThis folder should match the \"Root Output Path\" in the Wwise Project's SoundBanks settings.\nEnsure the folders match, and that SoundBanks and JSON metadata are generated.\nPress the \"Refresh\" button to re-parse the generated metadata."), FText::FromString(soundBankDirectory));
  854. return WarningText;
  855. }
  856. FText SWwiseBrowser::GetConnectionStatusText() const
  857. {
  858. switch(IsWaapiAvailable())
  859. {
  860. case Connected:
  861. return FText::Format(LOCTEXT("WwiseConnected", "Connected to Wwise Project: {0}"), GetProjectName());
  862. case SettingDisabled:
  863. return LOCTEXT("WwiseSettingDisabled", "Not Connected to Wwise. Only objects on disk shown. Enable \"Auto Connect to Waapi\" in the Wwise User Settings to see the Wwise project in the Browser.");
  864. case WrongProjectOpened:
  865. return LOCTEXT("WwiseWrongProject", "Not Connected to Wwise. Only objects on disk shown. The wrong Wwise project is opened.");
  866. case WwiseNotOpen:
  867. default:
  868. return LOCTEXT("WwiseNotConnected", "Not Connected to Wwise. Only objects on disk shown. Open Wwise to see the Wwise project in the Browser.");
  869. }
  870. }
  871. FText SWwiseBrowser::GetSoundBanksLocationText() const
  872. {
  873. return FText::Format(LOCTEXT("RootOutputPath", "Root Output Path: {0}"), FText::FromString(AkUnrealHelper::GetSoundBankDirectory()));
  874. }
  875. FReply SWwiseBrowser::OnOpenSettingsClicked()
  876. {
  877. FModuleManager::LoadModuleChecked<ISettingsModule>("Settings").ShowViewer(FName("Project"), FName("Wwise"), FName("Integration"));
  878. return FReply::Handled();
  879. }
  880. FReply SWwiseBrowser::OnRefreshClicked()
  881. {
  882. if (FModuleManager::Get().IsModuleLoaded("AudiokineticTools"))
  883. {
  884. UE_LOG(LogAudiokineticTools, Verbose, TEXT("SWwiseBrowser::OnRefreshClicked: Reloading project data."));
  885. FModuleManager::Get().GetModuleChecked<IAudiokineticTools>(FName("AudiokineticTools")).RefreshWwiseProject();
  886. }
  887. ForceRefresh();
  888. return FReply::Handled();
  889. }
  890. FReply SWwiseBrowser::OnGenerateSoundBanksClicked()
  891. {
  892. UE_LOG(LogAudiokineticTools, Verbose, TEXT("SWwiseBrowser::OnGenerateSoundBanksClicked: Opening Generate SoundBanks Window."));
  893. AkAudioBankGenerationHelper::CreateGenerateSoundDataWindow();
  894. return FReply::Handled();
  895. }
  896. void SWwiseBrowser::OnTreeItemDoubleClicked(FWwiseTreeItemPtr TreeItem)
  897. {
  898. if (TreeItem->IsBrowserType())
  899. {
  900. HandleFindInContentBrowserCommandExecute();
  901. }
  902. else if (TreeItem->IsFolder() && TreeItem->GetChildren().Num() > 0)
  903. {
  904. ExpandItem(TreeItem, !TreeViewPtr->IsItemExpanded(TreeItem));
  905. }
  906. }
  907. void SWwiseBrowser::ConstructTree()
  908. {
  909. UE_LOG(LogAudiokineticTools, Verbose, TEXT("SWwiseBrowser::ConstructTree: Rebuilding Wwise Browser Tree"));
  910. RootItems.Empty(EWwiseItemType::LastWwiseBrowserType - EWwiseItemType::Event + 1);
  911. for (int i = EWwiseItemType::Event; i <= EWwiseItemType::LastWwiseBrowserType + 1; ++i)
  912. {
  913. FWwiseTreeItemPtr NewRoot = DataSource->GetTreeRootForType(static_cast<EWwiseItemType::Type>(i));
  914. RootItems.Add(NewRoot);
  915. }
  916. RestoreTreeExpansion(RootItems);
  917. TreeViewPtr->RequestTreeRefresh();
  918. }
  919. void SWwiseBrowser::ExpandFirstLevel()
  920. {
  921. // Expand root items and first-level work units.
  922. for(int32 i = 0; i < RootItems.Num(); i++)
  923. {
  924. ExpandItem(RootItems[i], true);
  925. }
  926. }
  927. void SWwiseBrowser::ExpandParents(FWwiseTreeItemPtr Item)
  928. {
  929. if(Item->Parent.IsValid())
  930. {
  931. ExpandParents(Item->Parent.Pin());
  932. ExpandItem(Item->Parent.Pin(), true);
  933. }
  934. }
  935. TSharedRef<ITableRow> SWwiseBrowser::GenerateRow( FWwiseTreeItemPtr TreeItem, const TSharedRef<STableViewBase>& OwnerTable )
  936. {
  937. check(TreeItem.IsValid());
  938. TSharedRef<SWwiseBrowserTreeRow> NewRow = SNew(SWwiseBrowserTreeRow, TreeViewPtr.ToSharedRef(), SharedThis(this)).
  939. Item(TreeItem);
  940. TreeItem->TreeRow = NewRow;
  941. return NewRow;
  942. }
  943. void SWwiseBrowser::GetChildrenForTree(FWwiseTreeItemPtr TreeItem, TArray< FWwiseTreeItemPtr >& OutChildren)
  944. {
  945. if (TreeItem)
  946. {
  947. if(TreeItem->ChildCountInWwise)
  948. {
  949. if (!LastExpandedPaths.Contains(TreeItem->FolderPath))
  950. {
  951. DataSource->ClearEmptyChildren(TreeItem);
  952. // We add a placeholder item if the children exist, but are not loaded (e.g. for WAAPI). This should never be visible
  953. if (!TreeItem->GetChildren().Num())
  954. {
  955. FWwiseTreeItemPtr EmptyTreeItem = MakeShared<FWwiseTreeItem>(FString::Format(TEXT("Expansion placeholder for {0} "), { TreeItem->FolderPath }), "", nullptr, EWwiseItemType::None, FGuid());
  956. EmptyTreeItem->Parent = TreeItem;
  957. TreeItem->AddChild(EmptyTreeItem);
  958. }
  959. }
  960. else
  961. {
  962. ExpandItem(TreeItem, true);
  963. }
  964. }
  965. OutChildren = TreeItem->GetChildren();
  966. }
  967. }
  968. FReply SWwiseBrowser::OnDragDetected(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
  969. {
  970. return DoDragDetected(MouseEvent, TreeViewPtr->GetSelectedItems());
  971. }
  972. FReply SWwiseBrowser::DoDragDetected(const FPointerEvent& MouseEvent, const TArray<FWwiseTreeItemPtr>& SelectedItems)
  973. {
  974. if (!MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton))
  975. {
  976. return FReply::Unhandled();
  977. }
  978. if (SelectedItems.Num() == 0)
  979. {
  980. return FReply::Unhandled();
  981. }
  982. UE_LOG(LogAudiokineticTools, Verbose, TEXT("SWwiseBrowser::OnDragDetected: User drag operation started."));
  983. auto AkSettings = GetMutableDefault<UAkSettings>();
  984. const FString DefaultPath = AkSettings->DefaultAssetCreationPath;
  985. TArray<WwiseBrowserHelpers::WwiseBrowserAssetPayload> DragAssets;
  986. TSet<FGuid> SeenGuids;
  987. bool bAllItemsCanBeCreated = true;
  988. for (auto& WwiseTreeItem : SelectedItems)
  989. {
  990. if (!WwiseTreeItem->ItemId.IsValid() && !WwiseTreeItem->IsFolder())
  991. {
  992. UE_LOG(LogAudiokineticTools, Error, TEXT("Cannot drag selected Wwise asset: %s does not have a valid ID"), *(WwiseTreeItem->FolderPath));
  993. continue;
  994. }
  995. if(!WwiseBrowserHelpers::CanCreateAsset(WwiseTreeItem))
  996. {
  997. bAllItemsCanBeCreated = false;
  998. }
  999. WwiseBrowserHelpers::FindOrCreateAssetsRecursive(WwiseTreeItem,DragAssets,SeenGuids, WwiseBrowserHelpers::EAssetCreationMode::Transient);
  1000. }
  1001. if (DragAssets.Num() == 0 && bAllItemsCanBeCreated)
  1002. {
  1003. UE_LOG(LogAudiokineticTools, Error, TEXT("Failed to find or create Wwise asset '%s'%s in Browser operation"),
  1004. *(SelectedItems[0]->FolderPath), SelectedItems.Num() > 1 ? TEXT(" and others"): TEXT(""));
  1005. return FReply::Unhandled();
  1006. }
  1007. return FReply::Handled().BeginDragDrop(FWwiseAssetDragDropOp::New(DragAssets));
  1008. }
  1009. void SWwiseBrowser::ImportWwiseAssets(const TArray<FWwiseTreeItemPtr>& SelectedItems, const FString& PackagePath)
  1010. {
  1011. UE_LOG(LogAudiokineticTools, Verbose, TEXT("SWwiseBrowser::ImportWwiseAssets: Creating %d assets in %s."), SelectedItems.Num(), *PackagePath);
  1012. TArray<WwiseBrowserHelpers::WwiseBrowserAssetPayload> AssetsToImport;
  1013. TSet<FGuid> SeenGuids;
  1014. for(auto WwiseTreeItem: SelectedItems)
  1015. {
  1016. WwiseBrowserHelpers::FindOrCreateAssetsRecursive(WwiseTreeItem, AssetsToImport, SeenGuids,
  1017. WwiseBrowserHelpers::EAssetCreationMode::InPackage, PackagePath);
  1018. }
  1019. WwiseBrowserHelpers::SaveSelectedAssets(AssetsToImport, PackagePath,
  1020. WwiseBrowserHelpers::EAssetCreationMode::InPackage, WwiseBrowserHelpers::EAssetDuplicationMode::DoDuplication);
  1021. }
  1022. FReply SWwiseBrowser::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
  1023. {
  1024. const FKey KeyPressed = InKeyEvent.GetKey();
  1025. if (KeyPressed == EKeys::SpaceBar)
  1026. {
  1027. // Play the wwise item.
  1028. if (HandlePlayOrStopWwiseItemCanExecute())
  1029. {
  1030. HandlePlayWwiseItemCommandExecute();
  1031. return FReply::Handled();
  1032. }
  1033. }
  1034. else if (KeyPressed == EKeys::F5)
  1035. { // Populates the Wwise Browser.
  1036. HandleRefreshWwiseBrowserCommandExecute();
  1037. return FReply::Handled();
  1038. }
  1039. else if ((KeyPressed == EKeys::One) && InKeyEvent.IsControlDown() && InKeyEvent.IsShiftDown())
  1040. {
  1041. // Finds the specified object in the Project Explorer (Sync Group 1).
  1042. if (HandleFindInProjectExplorerWwiseItemCanExecute())
  1043. {
  1044. HandleFindInProjectExplorerWwiseItemCommandExecute();
  1045. return FReply::Handled();
  1046. }
  1047. }
  1048. return FReply::Unhandled();
  1049. }
  1050. void SWwiseBrowser::PopulateSearchStrings(const FString& FolderName, OUT TArray< FString >& OutSearchStrings) const
  1051. {
  1052. OutSearchStrings.Add(FolderName);
  1053. }
  1054. void SWwiseBrowser::OnSearchBoxChanged(const FText& InSearchText)
  1055. {
  1056. SearchBoxFilter->SetRawFilterText(InSearchText);
  1057. }
  1058. void SWwiseBrowser::SetItemVisibility(FWwiseTreeItemPtr Item, bool IsVisible)
  1059. {
  1060. if (Item.IsValid())
  1061. {
  1062. if (IsVisible)
  1063. {
  1064. // Propagate visibility to parents.
  1065. SetItemVisibility(Item->Parent.Pin(), IsVisible);
  1066. }
  1067. Item->IsVisible = IsVisible;
  1068. if (Item->TreeRow.IsValid())
  1069. {
  1070. TSharedRef<SWidget> wid = Item->TreeRow.Pin()->AsWidget();
  1071. wid->SetVisibility(IsVisible ? EVisibility::Visible : EVisibility::Collapsed);
  1072. }
  1073. }
  1074. }
  1075. void SWwiseBrowser::SaveCurrentTreeExpansion()
  1076. {
  1077. TSet<FWwiseTreeItemPtr> ExpandedItemSet;
  1078. TreeViewPtr->GetExpandedItems(ExpandedItemSet);
  1079. LastExpandedPaths.Empty();
  1080. for (const auto& Item : ExpandedItemSet)
  1081. {
  1082. if (Item.IsValid())
  1083. {
  1084. // Keep track of the last paths that we broadcasted for expansion reasons when filtering
  1085. LastExpandedPaths.Add(Item->FolderPath);
  1086. }
  1087. }
  1088. }
  1089. void SWwiseBrowser::RestoreTreeExpansion(const TArray< FWwiseTreeItemPtr >& Items)
  1090. {
  1091. for(int i = 0; i < Items.Num(); i++)
  1092. {
  1093. if (!Items[i])
  1094. {
  1095. continue;
  1096. }
  1097. if(!DataSource->IsKeptInBrowser(Items[i]))
  1098. {
  1099. continue;
  1100. }
  1101. // If filtering, everything should be expanded?
  1102. if (LastExpandedPaths.Contains(Items[i]->FolderPath) || IsFiltering())
  1103. {
  1104. ExpandItem(Items[i], true);
  1105. }
  1106. RestoreTreeExpansion(Items[i]->GetChildren());
  1107. }
  1108. }
  1109. void SWwiseBrowser::TreeSelectionChanged( FWwiseTreeItemPtr TreeItem, ESelectInfo::Type /*SelectInfo*/ )
  1110. {
  1111. if (AllowTreeViewDelegates)
  1112. {
  1113. const TArray<FWwiseTreeItemPtr> SelectedItems = TreeViewPtr->GetSelectedItems();
  1114. LastSelectedPaths.Empty();
  1115. for (int32 ItemIdx = 0; ItemIdx < SelectedItems.Num(); ++ItemIdx)
  1116. {
  1117. const FWwiseTreeItemPtr Item = SelectedItems[ItemIdx];
  1118. if (Item.IsValid())
  1119. {
  1120. LastSelectedPaths.Add(Item->FolderPath);
  1121. }
  1122. }
  1123. const UAkSettingsPerUser* AkSettingsPerUser = GetDefault<UAkSettingsPerUser>();
  1124. if (AkSettingsPerUser && AkSettingsPerUser->AutoSyncSelection)
  1125. {
  1126. DataSource->HandleFindWwiseItemInProjectExplorerCommandExecute(SelectedItems);
  1127. }
  1128. }
  1129. }
  1130. void SWwiseBrowser::TreeExpansionChanged( FWwiseTreeItemPtr TreeItem, bool bIsExpanded )
  1131. {
  1132. auto Items = TreeViewPtr->GetSelectedItems();
  1133. if (bIsExpanded)
  1134. {
  1135. // If we have a filter applied, it is assumed that everything is loaded
  1136. if (!IsFiltering())
  1137. {
  1138. TArray<FWwiseTreeItemPtr> OutChildren;
  1139. }
  1140. }
  1141. // If the item is not expanded we don't need to request the server to get any information(the children are hidden).
  1142. else
  1143. {
  1144. LastExpandedPaths.Remove(TreeItem->FolderPath);
  1145. return;
  1146. }
  1147. LastExpandedPaths.Add(TreeItem->FolderPath);
  1148. TreeViewPtr->SetSelectedItems(Items);
  1149. }
  1150. void SWwiseBrowser::ExpandItem(FWwiseTreeItemPtr TreeItem, bool bShouldExpand)
  1151. {
  1152. TreeViewPtr->SetItemExpansion(TreeItem, bShouldExpand);
  1153. TreeItem->IsExpanded = bShouldExpand;
  1154. }
  1155. void SWwiseBrowser::ExpandItem(FWwiseTreeItemPtr WaapiTreeItem)
  1156. {
  1157. if(auto TreeItem = GetTreeItemFromWaapiItem(WaapiTreeItem))
  1158. {
  1159. LastExpandedPaths.Add(TreeItem->FolderPath);
  1160. ExpandItem(TreeItem, true);
  1161. }
  1162. }
  1163. bool SWwiseBrowser::IsItemExpanded(FWwiseTreeItemPtr TreeItem)
  1164. {
  1165. return TreeViewPtr->IsItemExpanded(TreeItem);
  1166. }
  1167. void SWwiseBrowser::GetTreeItemsFromWaapi(const TArray<TSharedPtr<FWwiseTreeItem>>& WaapiTreeItems, TArray<TSharedPtr<FWwiseTreeItem>>& TreeItems)
  1168. {
  1169. for(auto& WaapiTreeItem : WaapiTreeItems)
  1170. {
  1171. if(auto TreeItem = GetTreeItemFromWaapiItem(WaapiTreeItem))
  1172. {
  1173. TreeItems.Add(TreeItem);
  1174. }
  1175. }
  1176. }
  1177. FWwiseTreeItemPtr SWwiseBrowser::GetTreeItemFromWaapiItem(FWwiseTreeItemPtr WaapiTreeItem)
  1178. {
  1179. for (auto& RootItem : RootItems)
  1180. {
  1181. if (auto ResultTreeItem = RootItem->FindItemRecursive(WaapiTreeItem))
  1182. {
  1183. return ResultTreeItem;
  1184. }
  1185. if (auto ResultTreeItem = RootItem->FindItemRecursive(WaapiTreeItem->FolderPath))
  1186. {
  1187. return ResultTreeItem;
  1188. }
  1189. }
  1190. return FWwiseTreeItemPtr();
  1191. }
  1192. void SWwiseBrowser::ExpandItems(const TArray<FWwiseTreeItemPtr>& Items)
  1193. {
  1194. for(auto& Item : Items)
  1195. {
  1196. ExpandItem(Item);
  1197. }
  1198. }
  1199. void SWwiseBrowser::UpdateWaapiSelection(const TArray<TSharedPtr<FWwiseTreeItem>>& WaapiTreeItems)
  1200. {
  1201. TArray<TSharedPtr<FWwiseTreeItem>> TreeItems;
  1202. GetTreeItemsFromWaapi(WaapiTreeItems, TreeItems);
  1203. TreeViewPtr->RequestTreeRefresh();
  1204. if (TreeItems.Num() > 0)
  1205. {
  1206. TreeViewPtr->ClearSelection();
  1207. TreeViewPtr->SetSelectedItems(TreeItems);
  1208. TreeViewPtr->RequestScrollIntoView(TreeItems[0]);
  1209. }
  1210. AllowTreeViewDelegates = true;
  1211. }
  1212. void SWwiseBrowser::HandleImportWwiseItemCommandExecute() const
  1213. {
  1214. // If not prompting individual files, prompt the user to select a target directory.
  1215. IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
  1216. if (DesktopPlatform)
  1217. {
  1218. FString LastWwiseImportPath = FEditorDirectories::Get().GetLastDirectory(ELastDirectory::GENERIC_IMPORT);
  1219. FString FolderName;
  1220. const FString Title = NSLOCTEXT("UnrealEd", "ChooseADirectory", "Choose A Directory").ToString();
  1221. const bool bFolderSelected = DesktopPlatform->OpenDirectoryDialog(
  1222. FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr),
  1223. Title,
  1224. LastWwiseImportPath,
  1225. FolderName
  1226. );
  1227. if (bFolderSelected)
  1228. {
  1229. if (!FPaths::IsUnderDirectory(FolderName, FPaths::ProjectContentDir()))
  1230. {
  1231. const FText FailReason = FText::FormatOrdered(LOCTEXT("CannotImportWwiseItem", "Cannot import into {0}. Folder must be in content directory."), FText::FromString(FolderName));
  1232. FMessageDialog::Open(EAppMsgType::Ok, FailReason);
  1233. UE_LOG(LogAudiokineticTools, Error, TEXT("%s"), *FailReason.ToString());
  1234. return;
  1235. }
  1236. FPaths::MakePathRelativeTo(FolderName, *FPaths::ProjectContentDir());
  1237. FString PackagePath = TEXT("/Game");
  1238. if (!FolderName.IsEmpty())
  1239. {
  1240. PackagePath = PackagePath / FolderName;
  1241. }
  1242. FEditorDirectories::Get().SetLastDirectory(ELastDirectory::GENERIC_IMPORT, PackagePath);
  1243. ImportWwiseAssets(TreeViewPtr->GetSelectedItems(), PackagePath);
  1244. }
  1245. }
  1246. }
  1247. void SWwiseBrowser::SetupColumns(SHeaderRow& HeaderRow)
  1248. {
  1249. Columns.Empty();
  1250. HeaderRow.ClearColumns();
  1251. auto WwiseObjectsColumn = MakeShared<FWwiseBrowserTreeColumn>(*this);
  1252. auto WwiseStatusColumn = MakeShared<FWwiseStatusColumn>(*this);
  1253. auto WwiseUEAssetStatusColumn = MakeShared<FWwiseUEAssetStatusColumn>();
  1254. auto SoundBankStatusColumn = MakeShared<FSoundBankStatusColumn>();
  1255. auto WwiseObjectArgs = WwiseObjectsColumn->ConstructHeaderRowColumn();
  1256. auto WwiseStatusArgs = WwiseStatusColumn->ConstructHeaderRowColumn();
  1257. auto WwiseUEAssetStatusArgs = WwiseUEAssetStatusColumn->ConstructHeaderRowColumn();
  1258. auto SoundBankStatusArgs = SoundBankStatusColumn->ConstructHeaderRowColumn();
  1259. Columns.Add(WwiseObjectArgs._ColumnId, WwiseObjectsColumn);
  1260. Columns.Add(WwiseStatusArgs._ColumnId, WwiseStatusColumn);
  1261. Columns.Add(WwiseUEAssetStatusArgs._ColumnId, WwiseUEAssetStatusColumn);
  1262. Columns.Add(SoundBankStatusArgs._ColumnId, SoundBankStatusColumn);
  1263. HeaderRowWidget->AddColumn(WwiseObjectArgs);
  1264. HeaderRowWidget->AddColumn(WwiseStatusArgs);
  1265. HeaderRowWidget->AddColumn(WwiseUEAssetStatusArgs);
  1266. HeaderRowWidget->AddColumn(SoundBankStatusArgs);
  1267. }
  1268. void SWwiseBrowser::OnTextFilterUpdated()
  1269. {
  1270. if (GetFilterText().IsEmpty())
  1271. {
  1272. bIsFilterApplied = false;
  1273. }
  1274. DataSource->ApplyTextFilter(SearchBoxFilter);
  1275. }
  1276. void SWwiseBrowser::OnFilterUpdated()
  1277. {
  1278. if (RootItems.Num() == 0)
  1279. {
  1280. return;
  1281. }
  1282. ApplyFilter();
  1283. TreeViewPtr->RequestTreeRefresh();
  1284. }
  1285. void SWwiseBrowser::ApplyFilter()
  1286. {
  1287. AllowTreeViewDelegates = false;
  1288. if (!bIsFilterApplied)
  1289. {
  1290. SaveCurrentTreeExpansion();
  1291. }
  1292. DataSource->ApplyFilter(SoundBankStatusFilter, UAssetStatusFilter, WwiseTypeFilter);
  1293. RootItems.Empty();
  1294. for (int i = EWwiseItemType::Event; i <= EWwiseItemType::LastWwiseBrowserType + 1; ++i)
  1295. {
  1296. FWwiseTreeItemPtr NewRoot = DataSource->GetTreeRootForType(static_cast<EWwiseItemType::Type>(i));
  1297. RootItems.Add(NewRoot);
  1298. }
  1299. bIsFilterApplied = true;
  1300. AllowTreeViewDelegates = true;
  1301. }
  1302. bool SWwiseBrowser::IsFiltering()
  1303. {
  1304. return !GetFilterText().IsEmpty();
  1305. }
  1306. FText SWwiseBrowser::GetFilterText()
  1307. {
  1308. return SearchBoxFilter.IsValid() ? SearchBoxFilter->GetRawFilterText(): FText();
  1309. }
  1310. FString SWwiseBrowser::GetFilterString()
  1311. {
  1312. return GetFilterText().ToString();
  1313. }
  1314. TAttribute<FText> SWwiseBrowser::GetFilterHighlightText() const
  1315. {
  1316. return SearchBoxFilter.IsValid() ? SearchBoxFilter->GetRawFilterText(): FText();
  1317. }
  1318. #undef LOCTEXT_NAMESPACE