/******************************************************************************* The content of this file includes portions of the proprietary AUDIOKINETIC Wwise Technology released in source code form as part of the game integration package. The content of this file may not be used without valid licenses to the AUDIOKINETIC Wwise Technology. Note that the use of the game engine is subject to the Unreal(R) Engine End User License Agreement at https://www.unrealengine.com/en-US/eula/unreal License Usage Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use this file in accordance with the end user license agreement provided with the software or, alternatively, in accordance with the terms contained in a written agreement between you and Audiokinetic Inc. Copyright (c) 2023 Audiokinetic Inc. *******************************************************************************/ /*------------------------------------------------------------------------------------ SWaapiPickerRow.cpp ------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------ includes. ------------------------------------------------------------------------------------*/ #include "WaapiPicker/SWaapiPickerRow.h" #include "AkAudioDevice.h" #include "AkWaapiUtils.h" #include "WaapiPicker/SWaapiPicker.h" #include "Widgets/Input/SSearchBox.h" #include "AkAudioStyle.h" #include "Widgets/Images/SImage.h" /*------------------------------------------------------------------------------------ Defines ------------------------------------------------------------------------------------*/ #define LOCTEXT_NAMESPACE "AkAudio" bool SWaapiPickerRow::CallWaapiExecuteUri(const char* inUri, const TArray& values, TSharedPtr& outJsonResult) { TSharedRef args = MakeShareable(new FJsonObject()); for (auto value : values) { args->SetStringField(value.keyArg, value.valueArg); } // Construct the options Json object; TSharedRef options = MakeShareable(new FJsonObject()); // Connect to Wwise Authoring on localhost. FAkWaapiClient* waapiClient = FAkWaapiClient::Get(); if (waapiClient) { // Request infos/changes in Waapi Picker using WAAPI if (waapiClient->Call(inUri, args, options, outJsonResult)) { return true; } else { UE_LOG(LogAkAudio, Log, TEXT("Call Failed")); } } else { UE_LOG(LogAkAudio, Log, TEXT("Unable to connect to localhost")); } return false; } /*------------------------------------------------------------------------------------ Implementation ------------------------------------------------------------------------------------*/ void SWaapiPickerRow::Construct(const FArguments& InArgs) { ParentWidget = InArgs._ParentWidget; WaapiPickerItem = InArgs._WaapiPickerItem; bool bIsRoot = !WaapiPickerItem.Pin()->Parent.IsValid() && ((WaapiPickerItem.Pin()->FolderPath == WwiseWaapiHelper::BACK_SLASH + EWwiseItemType::FolderNames[EWwiseItemType::Event]) || (WaapiPickerItem.Pin()->FolderPath == WwiseWaapiHelper::BACK_SLASH + EWwiseItemType::FolderNames[EWwiseItemType::AuxBus]) || (WaapiPickerItem.Pin()->FolderPath == WwiseWaapiHelper::BACK_SLASH + EWwiseItemType::FolderNames[EWwiseItemType::ActorMixer]) || (WaapiPickerItem.Pin()->FolderPath == WwiseWaapiHelper::BACK_SLASH + EWwiseItemType::FolderNames[EWwiseItemType::AcousticTexture]) || (WaapiPickerItem.Pin()->FolderPath == WwiseWaapiHelper::BACK_SLASH + EWwiseItemType::FolderNames[EWwiseItemType::EffectShareSet])); ChildSlot [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .Padding(0, bIsRoot? 2 : 1, 2, 1) .VAlign(VAlign_Center) [ // Item Icon SNew(SImage) .Image(FAkAudioStyle::GetBrush(WaapiPickerItem.Pin()->ItemType)) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) [ SAssignNew(InlineRenameWidget, SInlineEditableTextBlock) .Text(this, &SWaapiPickerRow::GetNameText) .ToolTipText(this, &SWaapiPickerRow::GetToolTipText) .HighlightText(InArgs._HighlightText) .OnTextCommitted(this, &SWaapiPickerRow::HandleNameCommitted) .OnVerifyTextChanged(this, &SWaapiPickerRow::HandleVerifyNameChanged) .IsSelected(InArgs._IsSelected) .IsReadOnly(this, &SWaapiPickerRow::IsWiseItemNameReadOnly) ] ]; if (InlineRenameWidget.IsValid()) { EnterEditingModeDelegateHandle = OnRenamedRequestEvent.AddSP(InlineRenameWidget.Get(), &SInlineEditableTextBlock::EnterEditingMode); } } void SWaapiPickerRow::EnterEditingMode() { InlineRenameWidget->EnterEditingMode(); } SWaapiPickerRow::~SWaapiPickerRow() { if (InlineRenameWidget.IsValid()) { OnRenamedRequestEvent.Remove(EnterEditingModeDelegateHandle); } } void SWaapiPickerRow::HandleNameCommitted(const FText& NewText, ETextCommit::Type CommitInfo) { TSharedPtr WaapiPickerItemPtr = WaapiPickerItem.Pin(); if (WaapiPickerItemPtr.IsValid()) { FText WarningMessage; const bool bIsCommitted = (CommitInfo != ETextCommit::OnCleared); if (!OnItemRenameCommitted(WaapiPickerItemPtr, NewText.ToString(), WarningMessage) && ParentWidget.IsValid() && bIsCommitted) { // Failed to rename/create a WaapiPickerItem, display a warning. UE_LOG(LogAkAudio, Log, TEXT("Rename Failed : %s"), *WarningMessage.ToString()); } } } bool SWaapiPickerRow::OnItemRenameCommitted(const TSharedPtr< FWwiseTreeItem >& WwiseItem, const FString& InNewItemName, FText& OutWarningMessage) { if (WwiseItem.IsValid() && !InNewItemName.Equals(WwiseItem->DisplayName, ESearchCase::CaseSensitive)) { #if AK_SUPPORT_WAAPI const FGuid& in_ItemId = WwiseItem->ItemId; const FString itemIdStringField = in_ItemId.ToString(EGuidFormats::DigitsWithHyphensInBraces); TSharedPtr getResult; if (CallWaapiExecuteUri(ak::wwise::core::object::setName, { {WwiseWaapiHelper::OBJECT, itemIdStringField} , {WwiseWaapiHelper::VALUE, InNewItemName } }, getResult)) { return true; } #endif } return false; } bool SWaapiPickerRow::HandleVerifyNameChanged(const FText& NewText, FText& OutErrorMessage) { TSharedPtr WaapiPickerItemPtr = WaapiPickerItem.Pin(); if (WaapiPickerItemPtr.IsValid()) { return OnVerifyItemNameChanged(WaapiPickerItemPtr, NewText.ToString(), OutErrorMessage); } return false; } bool SWaapiPickerRow::OnVerifyItemNameChanged(const TSharedPtr< FWwiseTreeItem >& WwiseItem, const FString& InNewItemName, FText& OutErrorMessage) { if (!WwiseItem.IsValid()) { OutErrorMessage = LOCTEXT("RenameFailed_TreeItemDeleted", "Tree item no longer exists"); return false; } if ((WwiseItem->ItemType == EWwiseItemType::PhysicalFolder) || (WwiseItem->ItemType == EWwiseItemType::StandaloneWorkUnit) || (WwiseItem->ItemType == EWwiseItemType::NestedWorkUnit)) { OutErrorMessage = LOCTEXT("RenameFailed_WorkUnitItem", "You can't change the name of a PhysicalFolder/WorkUnit"); return false; } if (WwiseItem->Parent == NULL) { OutErrorMessage = LOCTEXT("RenameFailed_RootTreeItem", "A root tree item can not be renamed"); return false; } FText TrimmedLabel = FText::TrimPrecedingAndTrailing(FText::FromString(InNewItemName)); if (TrimmedLabel.IsEmpty()) { OutErrorMessage = LOCTEXT("RenameFailed_LeftBlank", "Names cannot be left blank"); return false; } if (TrimmedLabel.ToString().Len() >= NAME_SIZE) { FFormatNamedArguments Arguments; Arguments.Add(TEXT("CharCount"), NAME_SIZE); OutErrorMessage = FText::Format(LOCTEXT("RenameFailed_TooLong", "Names must be less than {CharCount} characters long."), Arguments); return false; } // If the new name is the same as the old name, consider this to be unchanged, and accept it. const FString& LabelString = TrimmedLabel.ToString(); if (WwiseItem->DisplayName == LabelString) { return true; } int32 Dummy = 0; if (LabelString.FindChar('/', Dummy) || LabelString.FindChar('\\', Dummy)) { OutErrorMessage = LOCTEXT("RenameFailed_InvalidChar", "Item names cannot contain / or \\."); return false; } // Validate that this folder doesn't exist already FName NewPath = FName(*(WwiseItem->Parent.Pin()->FolderPath)); NewPath = FName(*(NewPath.ToString() + WwiseWaapiHelper::BACK_SLASH + LabelString)); if (WwiseItem->Parent.Pin()->GetChild(InNewItemName).IsValid()) { OutErrorMessage = LOCTEXT("RenameFailed_AlreadyExists", "An item with this name already exists at this level"); return false; } return true; } bool SWaapiPickerRow::IsWiseItemNameReadOnly() const { // We can't rename roots or Wise items of type "PhysicalFolder || StandaloneWorkUnit || NestedWorkUnit" const TSharedPtr& WaapiPickerItemPtr = WaapiPickerItem.Pin(); return ((WaapiPickerItemPtr->Parent == NULL) || (WaapiPickerItemPtr->ItemType == EWwiseItemType::PhysicalFolder) || (WaapiPickerItemPtr->ItemType == EWwiseItemType::StandaloneWorkUnit) || (WaapiPickerItemPtr->ItemType == EWwiseItemType::NestedWorkUnit)); } FText SWaapiPickerRow::GetNameText() const { const TSharedPtr& WaapiPickerItemPtr = WaapiPickerItem.Pin(); if (WaapiPickerItemPtr.IsValid()) { return FText::FromString(WaapiPickerItemPtr->DisplayName); } else { return FText(); } } FText SWaapiPickerRow::GetToolTipText() const { const TSharedPtr& WaapiPickerItemPtr = WaapiPickerItem.Pin(); if (WaapiPickerItemPtr.IsValid()) { return FText::FromString(WaapiPickerItemPtr->FolderPath); } else { return FText(); } } #undef LOCTEXT_NAMESPACE