AkCheckBox.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  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. #include "AkWaapiUMG/Components/AkCheckBox.h"
  16. #include "AkAudioDevice.h"
  17. #include "Widgets/SNullWidget.h"
  18. #include "Widgets/DeclarativeSyntaxSupport.h"
  19. #include "Widgets/Input/SCheckBox.h"
  20. #include "AkWaapiUMG/Components/AkBoolPropertyToControlCustomization.h"
  21. #include "AkWaapiUMG/Components/WwiseBoolPropertyDragDropOp.h"
  22. #include "AkWaapiUtils.h"
  23. #include "AkWaapiBlueprints/AkWaapiCalls.h"
  24. #include "Async/Async.h"
  25. #include "AkWaapiUMG/Components/WwiseUmgDragDropOp.h"
  26. #include "WaapiPicker/SWaapiPicker.h"
  27. #if WITH_EDITOR
  28. #include "Modules/ModuleManager.h"
  29. #include "PropertyEditorModule.h"
  30. #endif
  31. #define LOCTEXT_NAMESPACE "AkWaapiUMG"
  32. ////////////////////////////////////////////////
  33. // SCheckBoxDropHandler
  34. ////////////////////////////////////////////////
  35. /** Drag-drop zone for adding a Wwise item or a bool property to the CheckBox */
  36. class SCheckBoxDropHandler : public SCompoundWidget
  37. {
  38. public:
  39. SLATE_BEGIN_ARGS(SCheckBoxDropHandler) {}
  40. SLATE_DEFAULT_SLOT(FArguments, Content)
  41. SLATE_EVENT(FOnDrop, OnDrop)
  42. SLATE_END_ARGS()
  43. void Construct(const FArguments& InArgs)
  44. {
  45. OnDropDelegate = InArgs._OnDrop;
  46. this->ChildSlot
  47. [
  48. SNew(SBorder)
  49. [
  50. InArgs._Content.Widget
  51. ]
  52. ];
  53. }
  54. FReply OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override
  55. {
  56. if (OnDropDelegate.IsBound())
  57. {
  58. return OnDropDelegate.Execute(MyGeometry, DragDropEvent);
  59. }
  60. return FReply::Handled();
  61. }
  62. private:
  63. FOnDrop OnDropDelegate;
  64. };
  65. /////////////////////////////////////////////////////
  66. // UAkCheckBox
  67. UAkCheckBox::UAkCheckBox(const FObjectInitializer& ObjectInitializer)
  68. : Super(ObjectInitializer)
  69. {
  70. SCheckBox::FArguments SlateDefaults;
  71. WidgetStyle = *SlateDefaults._Style;
  72. CheckedState = ECheckBoxState::Unchecked;
  73. HorizontalAlignment = SlateDefaults._HAlign;
  74. IsFocusable = true;
  75. }
  76. void UAkCheckBox::BeginDestroy()
  77. {
  78. if (SubscriptionIdNameChanged != 0)
  79. {
  80. bool isUnsubscribed;
  81. UAkWaapiCalls::Unsubscribe(FAkWaapiSubscriptionId(SubscriptionIdNameChanged), isUnsubscribed);
  82. if (isUnsubscribed)
  83. SubscriptionIdNameChanged = 0;
  84. }
  85. if (SubscriptionId != 0)
  86. {
  87. bool isUnsubscribed;
  88. UAkWaapiCalls::Unsubscribe(FAkWaapiSubscriptionId(SubscriptionId), isUnsubscribed);
  89. if (isUnsubscribed)
  90. SubscriptionId = 0;
  91. }
  92. Super::BeginDestroy();
  93. }
  94. void UAkCheckBox::ReleaseSlateResources(bool bReleaseChildren)
  95. {
  96. Super::ReleaseSlateResources(bReleaseChildren);
  97. MyCheckbox.Reset();
  98. }
  99. TSharedRef<SWidget> UAkCheckBox::RebuildWidget()
  100. {
  101. #if WITH_EDITOR
  102. FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
  103. PropertyModule.RegisterCustomPropertyTypeLayout("AkBoolPropertyToControl", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FAkBoolPropertyToControlCustomization::MakeInstance));
  104. PropertyModule.NotifyCustomizationModuleChanged();
  105. #endif//WITH_EDITOR
  106. MyCheckbox = SNew(SCheckBox)
  107. .OnCheckStateChanged(BIND_UOBJECT_DELEGATE(FOnCheckStateChanged, SlateOnCheckStateChangedCallback))
  108. .Style(&WidgetStyle)
  109. .HAlign(HorizontalAlignment)
  110. .IsFocusable(IsFocusable)
  111. ;
  112. if (GetChildrenCount() > 0)
  113. {
  114. MyCheckbox->SetContent(GetContentSlot()->Content ? GetContentSlot()->Content->TakeWidget() : SNullWidget::NullWidget);
  115. }
  116. return
  117. SNew(SCheckBoxDropHandler)
  118. .OnDrop(FOnDrop::CreateUObject(this, &UAkCheckBox::OnDropHandler))
  119. [
  120. SNew(SHorizontalBox)
  121. + SHorizontalBox::Slot()
  122. .AutoWidth()
  123. .Padding(FMargin(3.0f, 3.0f))
  124. .HAlign(HAlign_Center)
  125. .VAlign(VAlign_Center)
  126. [
  127. MyCheckbox.ToSharedRef()
  128. ]
  129. + SHorizontalBox::Slot()
  130. .HAlign(HAlign_Left)
  131. .VAlign(VAlign_Center)
  132. [
  133. SNew(SVerticalBox)
  134. + SVerticalBox::Slot()
  135. .AutoHeight()
  136. .Padding(5.0f, 0.0f, 0.0f, 0.0f)
  137. .HAlign(HAlign_Left)
  138. [
  139. SNew(STextBlock)
  140. .Text(TAttribute<FText>::Create(TAttribute<FText>::FGetter::CreateUObject(this, &UAkCheckBox::GetAkItemControlled)))
  141. ]
  142. + SVerticalBox::Slot()
  143. .AutoHeight()
  144. .Padding(5.0f, 0.0f, 0.0f, 0.0f)
  145. .HAlign(HAlign_Left)
  146. [
  147. SNew(STextBlock)
  148. .Text(TAttribute<FText>::Create(TAttribute<FText>::FGetter::CreateUObject(this, &UAkCheckBox::GetAkBoolProperty)))
  149. ]
  150. ]
  151. ];
  152. }
  153. void UAkCheckBox::SynchronizeProperties()
  154. {
  155. Super::SynchronizeProperties();
  156. MyCheckbox->SetStyle(&WidgetStyle);
  157. MyCheckbox->SetIsChecked(PROPERTY_BINDING(ECheckBoxState, CheckedState) );
  158. ItemToControl.ItemPath = ItemToControl.ItemPicked.ItemPath;
  159. FGuid ItemId;
  160. if (!ItemToControl.ItemPicked.ItemId.IsEmpty())
  161. {
  162. if(FGuid::ParseExact(ItemToControl.ItemPicked.ItemId, EGuidFormats::DigitsWithHyphensInBraces, ItemId))
  163. {
  164. SetAkItemId(ItemId);
  165. }
  166. }
  167. SetAkBoolProperty(ThePropertyToControl.ItemProperty);
  168. }
  169. void UAkCheckBox::SetAkItemControlled(const FString& Item)
  170. {
  171. ItemControlled = Item;
  172. }
  173. FText UAkCheckBox::GetAkItemControlled()
  174. {
  175. return FText::FromString(TEXT("Item : ") + ItemControlled);
  176. }
  177. void UAkCheckBox::SetAkItemId(const FGuid& ItemId)
  178. {
  179. if (ItemId.IsValid())
  180. {
  181. IdItemToControl = ItemId.ToString(EGuidFormats::DigitsWithHyphensInBraces);
  182. TSharedPtr<FJsonObject> getResult;
  183. if (!SWaapiPicker::CallWaapiGetInfoFrom(WwiseWaapiHelper::ID, IdItemToControl, getResult, {}))
  184. {
  185. return;
  186. }
  187. TArray<TSharedPtr<FJsonValue>> StructJsonArray = getResult->GetArrayField(WwiseWaapiHelper::RETURN);
  188. if (StructJsonArray.Num() > 0)
  189. {
  190. const TSharedPtr<FJsonObject>& ItemInfoObj = StructJsonArray[0]->AsObject();
  191. SetAkItemControlled(ItemInfoObj->GetStringField(WwiseWaapiHelper::NAME));
  192. }
  193. #if AK_SUPPORT_WAAPI
  194. /** UnSubscribe to object renamed to be notified from Wwise using WAAPI, so we can maintain the name of the item controlled up to date dynamically. */
  195. // Connect to Wwise Authoring on localhost.
  196. if (SubscriptionIdNameChanged != 0)
  197. {
  198. bool isUnsubscribed;
  199. UAkWaapiCalls::Unsubscribe(FAkWaapiSubscriptionId(SubscriptionIdNameChanged), isUnsubscribed);
  200. if (isUnsubscribed)
  201. SubscriptionIdNameChanged = 0;
  202. }
  203. TSharedPtr<FJsonObject> outJsonResult;
  204. /** Subscribe to object renamed-created-deleted and removed to be notified from Wwise using WAAPI, so we can maintain the Wise picker up to date dynamically. */
  205. // Connect to Wwise Authoring on localhost.
  206. FAkWaapiClient* waapiClient = FAkWaapiClient::Get();
  207. if (waapiClient)
  208. {
  209. auto wampEventCallback = WampEventCallback::CreateLambda([this](uint64_t id, TSharedPtr<FJsonObject> in_UEJsonObject)
  210. {
  211. AsyncTask(ENamedThreads::GameThread, [this, in_UEJsonObject]
  212. {
  213. SetAkItemControlled(in_UEJsonObject->GetStringField(WwiseWaapiHelper::NEW_NAME));
  214. });
  215. });
  216. // Construct the options Json object : Getting parent infos.
  217. TSharedRef<FJsonObject> in_options = MakeShareable(new FJsonObject());
  218. // Subscribe to object renamed-created-deleted and removed notifications.
  219. waapiClient->Subscribe(ak::wwise::core::object::nameChanged, in_options, wampEventCallback, SubscriptionIdNameChanged, outJsonResult);
  220. }
  221. SynchronizePropertyWithWwise();
  222. #endif
  223. }
  224. }
  225. const FGuid UAkCheckBox::GetAkItemId() const
  226. {
  227. FGuid ItemId;
  228. if (!IdItemToControl.IsEmpty())
  229. {
  230. FGuid::ParseExact(IdItemToControl, EGuidFormats::DigitsWithHyphensInBraces, ItemId);
  231. }
  232. return ItemId;
  233. }
  234. void UAkCheckBox::SetAkBoolProperty(const FString& BoolProperty)
  235. {
  236. BoolPropertyToControl = BoolProperty;
  237. SynchronizePropertyWithWwise();
  238. }
  239. const FString UAkCheckBox::GetAkProperty() const
  240. {
  241. return BoolPropertyToControl;
  242. }
  243. FText UAkCheckBox::GetAkBoolProperty() const
  244. {
  245. return FText::FromString(TEXT("Property : ") + GetAkProperty());
  246. }
  247. void UAkCheckBox::OnSlotAdded(UPanelSlot* InSlot)
  248. {
  249. // Add the child to the live slot if it already exists
  250. if ( MyCheckbox.IsValid() )
  251. {
  252. MyCheckbox->SetContent(InSlot->Content ? InSlot->Content->TakeWidget() : SNullWidget::NullWidget);
  253. }
  254. }
  255. void UAkCheckBox::SynchronizePropertyWithWwise()
  256. {
  257. if (!IdItemToControl.IsEmpty() && !BoolPropertyToControl.IsEmpty())
  258. {
  259. TSharedPtr<FJsonObject> ItemInfoResult;
  260. if (CallWappiGetPropertySate(IdItemToControl, BoolPropertyToControl, ItemInfoResult))
  261. {
  262. bool result = false;
  263. if (ItemInfoResult.Get()->TryGetBoolField(WwiseWaapiHelper::AT + BoolPropertyToControl, result))
  264. {
  265. SetIsChecked(result);
  266. }
  267. }
  268. if (SubscriptionId != 0)
  269. {
  270. bool isUnsubscribed;
  271. UAkWaapiCalls::Unsubscribe(FAkWaapiSubscriptionId(SubscriptionId), isUnsubscribed);
  272. if (isUnsubscribed)
  273. SubscriptionId = 0;
  274. }
  275. TSharedPtr<FJsonObject> outJsonResult;
  276. auto wampEventCallback = WampEventCallback::CreateLambda([this](uint64_t id, TSharedPtr<FJsonObject> in_UEJsonObject)
  277. {
  278. bool result = false;
  279. if (in_UEJsonObject->TryGetBoolField(WwiseWaapiHelper::NEW, result))
  280. {
  281. SetIsChecked(result);
  282. }
  283. });
  284. SubscribeToPropertyStateChange(IdItemToControl, BoolPropertyToControl, wampEventCallback, SubscriptionId, outJsonResult);
  285. }
  286. }
  287. void UAkCheckBox::OnSlotRemoved(UPanelSlot* InSlot)
  288. {
  289. // Remove the widget from the live slot if it exists.
  290. if ( MyCheckbox.IsValid() )
  291. {
  292. MyCheckbox->SetContent(SNullWidget::NullWidget);
  293. }
  294. }
  295. bool UAkCheckBox::IsPressed() const
  296. {
  297. if ( MyCheckbox.IsValid() )
  298. {
  299. return MyCheckbox->IsPressed();
  300. }
  301. return false;
  302. }
  303. bool UAkCheckBox::IsChecked() const
  304. {
  305. if ( MyCheckbox.IsValid() )
  306. {
  307. return MyCheckbox->IsChecked();
  308. }
  309. return ( CheckedState == ECheckBoxState::Checked );
  310. }
  311. ECheckBoxState UAkCheckBox::GetCheckedState() const
  312. {
  313. if ( MyCheckbox.IsValid() )
  314. {
  315. return MyCheckbox->GetCheckedState();
  316. }
  317. return CheckedState;
  318. }
  319. void UAkCheckBox::SetIsChecked(bool InIsChecked)
  320. {
  321. CheckedState = InIsChecked ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
  322. if ( MyCheckbox.IsValid() )
  323. {
  324. MyCheckbox->SetIsChecked(PROPERTY_BINDING(ECheckBoxState, CheckedState));
  325. }
  326. }
  327. void UAkCheckBox::SetCheckedState(ECheckBoxState InCheckedState)
  328. {
  329. CheckedState = InCheckedState;
  330. if ( MyCheckbox.IsValid() )
  331. {
  332. MyCheckbox->SetIsChecked(PROPERTY_BINDING(ECheckBoxState, CheckedState));
  333. }
  334. }
  335. void UAkCheckBox::SlateOnCheckStateChangedCallback(ECheckBoxState NewState)
  336. {
  337. CheckedState = NewState;
  338. if (ItemControlled.IsEmpty() || BoolPropertyToControl.IsEmpty() || IdItemToControl.IsEmpty())
  339. {
  340. UE_LOG(LogAkAudio, Log, TEXT("No item or property to control"));
  341. return;
  342. }
  343. // Construct the arguments Json object : setting configs
  344. TSharedRef<FJsonObject> args = MakeShareable(new FJsonObject());
  345. {
  346. args->SetStringField(WwiseWaapiHelper::OBJECT, IdItemToControl);
  347. args->SetStringField(WwiseWaapiHelper::PROPERTY, BoolPropertyToControl);
  348. args->SetBoolField(WwiseWaapiHelper::VALUE, IsChecked()); // this gives us a range of volume from [-96 to 12]
  349. }
  350. // Construct the options Json object;
  351. TSharedRef<FJsonObject> options = MakeShareable(new FJsonObject());
  352. // Connect to Wwise Authoring on localhost.
  353. FAkWaapiClient* waapiClient = FAkWaapiClient::Get();
  354. if (waapiClient)
  355. {
  356. #if AK_SUPPORT_WAAPI
  357. TSharedPtr<FJsonObject> outJsonResult;
  358. // Request data from Wwise using WAAPI
  359. if (waapiClient->Call(ak::wwise::core::object::setProperty, args, options, outJsonResult))
  360. {
  361. }
  362. else
  363. {
  364. UE_LOG(LogAkAudio, Log, TEXT("Call Failed"));
  365. return;
  366. }
  367. #endif
  368. }
  369. else
  370. {
  371. UE_LOG(LogAkAudio, Log, TEXT("Unable to connect to localhost"));
  372. return;
  373. }
  374. // Choosing to treat Undetermined as Checked
  375. const bool bWantsToBeChecked = NewState != ECheckBoxState::Unchecked;
  376. AkOnCheckStateChanged.Broadcast(bWantsToBeChecked);
  377. }
  378. FReply UAkCheckBox::OnDropHandler(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
  379. {
  380. FReply HandledState = FReply::Unhandled();
  381. TSharedPtr<FDragDropOperation> Operation = DragDropEvent.GetOperation();
  382. if (Operation.IsValid())
  383. {
  384. if (Operation->IsOfType<FWwiseUmgDragDropOp>())
  385. {
  386. const auto& AssetDragDropOp = StaticCastSharedPtr<FWwiseUmgDragDropOp>(Operation);
  387. if (AssetDragDropOp.IsValid())
  388. {
  389. const auto& WwiseAssets = AssetDragDropOp->GetWiseItems();
  390. if (WwiseAssets.Num() && WwiseAssets[0].IsValid())
  391. {
  392. if (OnItemDropped.IsBound())
  393. {
  394. OnItemDropped.Broadcast(WwiseAssets[0]->ItemId);
  395. }
  396. else
  397. {
  398. SetAkItemId(WwiseAssets[0]->ItemId);
  399. }
  400. HandledState = FReply::Handled();
  401. }
  402. }
  403. }
  404. else if (Operation->IsOfType<FWwiseBoolPropertyDragDropOp>())
  405. {
  406. const auto& AssetDragDropOp = StaticCastSharedPtr<FWwiseBoolPropertyDragDropOp>(Operation);
  407. if (AssetDragDropOp.IsValid())
  408. {
  409. const auto& WwiseAssets = AssetDragDropOp->GetWiseProperties();
  410. if (WwiseAssets.Num() && WwiseAssets[0].IsValid())
  411. {
  412. if (OnPropertyDropped.IsBound())
  413. {
  414. OnPropertyDropped.Broadcast(*WwiseAssets[0].Get());
  415. }
  416. else
  417. {
  418. SetAkBoolProperty(*WwiseAssets[0].Get());
  419. }
  420. HandledState = FReply::Handled();
  421. }
  422. }
  423. }
  424. }
  425. return HandledState;
  426. }
  427. #if WITH_EDITOR
  428. const FText UAkCheckBox::GetPaletteCategory()
  429. {
  430. return LOCTEXT("Wwise", "Wwise");
  431. }
  432. #endif
  433. /////////////////////////////////////////////////////
  434. #undef LOCTEXT_NAMESPACE