Files
UnrealEngine/Engine/Source/Editor/AudioEditor/Private/SoundSubmixGraphNode.cpp
2025-05-18 13:04:45 +08:00

264 lines
6.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SoundSubmixGraph/SoundSubmixGraphNode.h"
#include "Audio/AudioWidgetSubsystem.h"
#include "Audio/SoundSubmixWidgetInterface.h"
#include "Blueprint/UserWidget.h"
#include "Containers/Array.h"
#include "Containers/EnumAsByte.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraph/EdGraphSchema.h"
#include "Editor.h"
#include "Editor/EditorEngine.h"
#include "Engine/Engine.h"
#include "HAL/PlatformMath.h"
#include "Internationalization/Internationalization.h"
#include "Layout/Margin.h"
#include "Misc/AssertionMacros.h"
#include "Misc/Attribute.h"
#include "SlotBase.h"
#include "Sound/SoundSubmix.h"
#include "SoundSubmixDefaultColorPalette.h"
#include "SoundSubmixEditor.h"
#include "SoundSubmixGraph/SoundSubmixGraph.h"
#include "SoundSubmixGraph/SoundSubmixGraphSchema.h"
#include "Styling/AppStyle.h"
#include "Subsystems/AssetEditorSubsystem.h"
#include "Templates/Casts.h"
#include "Types/SlateEnums.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/SBoxPanel.h"
class SWidget;
class UWorld;
#define LOCTEXT_NAMESPACE "SoundSubmixGraphNode"
void SSubmixGraphNode::Construct(const FArguments& InArgs, UEdGraphNode* InGraphNode)
{
SubmixBase = InArgs._SubmixBase;
SubmixNodeUserWidget = InArgs._SubmixNodeUserWidget;
if (SubmixNodeUserWidget.IsValid())
{
ISoundSubmixWidgetInterface::Execute_OnConstructed(SubmixNodeUserWidget.Get(), SubmixBase.Get());
}
GraphNode = InGraphNode;
UpdateGraphNode();
}
TSharedRef<SWidget> SSubmixGraphNode::CreateNodeContentArea()
{
if (SubmixNodeUserWidget.IsValid())
{
// NODE CONTENT AREA
return SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("NoBorder"))
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(FMargin(0, 3))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.HAlign(HAlign_Left)
.FillWidth(1.0f)
[
// LEFT
SAssignNew(LeftNodeBox, SVerticalBox)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Right)
[
// RIGHT
SAssignNew(RightNodeBox, SVerticalBox)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.HAlign(HAlign_Left)
.FillWidth(1.0f)
[
SubmixNodeUserWidget->TakeWidget()
]
]
]
;
}
else
{
return SGraphNode::CreateNodeContentArea();
}
}
USoundSubmixGraphNode::USoundSubmixGraphNode(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, ChildPin(NULL)
, ParentPin(NULL)
{
}
bool USoundSubmixGraphNode::CheckRepresentsSoundSubmix()
{
if (!SoundSubmix)
{
return false;
}
for (int32 ChildIndex = 0; ChildIndex < ChildPin->LinkedTo.Num(); ChildIndex++)
{
USoundSubmixGraphNode* ChildNode = CastChecked<USoundSubmixGraphNode>(ChildPin->LinkedTo[ChildIndex]->GetOwningNode());
if (!SoundSubmix->ChildSubmixes.Contains(ChildNode->SoundSubmix))
{
return false;
}
}
for (int32 ChildIndex = 0; ChildIndex < SoundSubmix->ChildSubmixes.Num(); ChildIndex++)
{
bool bFoundChild = false;
for (int32 NodeChildIndex = 0; NodeChildIndex < ChildPin->LinkedTo.Num(); NodeChildIndex++)
{
USoundSubmixGraphNode* ChildNode = CastChecked<USoundSubmixGraphNode>(ChildPin->LinkedTo[NodeChildIndex]->GetOwningNode());
if (ChildNode->SoundSubmix == SoundSubmix->ChildSubmixes[ChildIndex])
{
bFoundChild = true;
break;
}
}
if (!bFoundChild)
{
return false;
}
}
return true;
}
FLinearColor USoundSubmixGraphNode::GetNodeTitleColor() const
{
return Audio::GetColorForSubmixType(SoundSubmix);
}
void USoundSubmixGraphNode::AllocateDefaultPins()
{
check(Pins.Num() == 0);
ChildPin = CreatePin(EGPD_Input, Audio::GetNameForSubmixType(SoundSubmix), *LOCTEXT("SoundSubmixGraphNode_Input", "Input").ToString());
if (USoundSubmixWithParentBase* NonEndpointSubmix = Cast<USoundSubmixWithParentBase>(SoundSubmix))
{
ParentPin = CreatePin(EGPD_Output, Audio::GetNameForSubmixType(SoundSubmix), *LOCTEXT("SoundSubmixGraphNode_Output", "Output").ToString());
}
}
void USoundSubmixGraphNode::AutowireNewNode(UEdGraphPin* FromPin)
{
if (FromPin)
{
const USoundSubmixGraphSchema* Schema = CastChecked<USoundSubmixGraphSchema>(GetSchema());
if (FromPin->Direction == EGPD_Input)
{
Schema->TryCreateConnection(FromPin, ParentPin);
}
else
{
Schema->TryCreateConnection(FromPin, ChildPin);
}
}
}
bool USoundSubmixGraphNode::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const
{
return Schema->IsA(USoundSubmixGraphSchema::StaticClass());
}
bool USoundSubmixGraphNode::CanUserDeleteNode() const
{
check(GEditor);
UAssetEditorSubsystem* EditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
TArray<IAssetEditorInstance*> SubmixEditors = EditorSubsystem->FindEditorsForAsset(SoundSubmix);
for (IAssetEditorInstance* Editor : SubmixEditors)
{
if (!Editor)
{
continue;
}
FSoundSubmixEditor* SubmixEditor = static_cast<FSoundSubmixEditor*>(Editor);
if (UEdGraph* Graph = SubmixEditor->GetGraph())
{
if (SoundSubmix->SoundSubmixGraph == Graph)
{
USoundSubmixBase* RootSubmix = CastChecked<USoundSubmixGraph>(Graph)->GetRootSoundSubmix();
if (RootSubmix == SoundSubmix)
{
return false;
}
}
}
}
return UEdGraphNode::CanUserDeleteNode();
}
FText USoundSubmixGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (SoundSubmix)
{
return FText::FromString(SoundSubmix->GetName());
}
else
{
return Super::GetNodeTitle(TitleType);
}
}
TSharedPtr<SGraphNode> USoundSubmixGraphNode::CreateVisualWidget()
{
if (UWorld* World = GEditor ? GEditor->GetEditorWorldContext().World() : nullptr)
{
if (USoundSubmixBase* SubmixBase = Cast<USoundSubmixBase>(SoundSubmix))
{
if (UAudioWidgetSubsystem* AudioWidgetSubsystem = GEngine ? GEngine->GetEngineSubsystem<UAudioWidgetSubsystem>() : nullptr)
{
TArray<UUserWidget*> UserWidgets = AudioWidgetSubsystem->CreateUserWidgets(*World, USoundSubmixWidgetInterface::StaticClass());
if (!UserWidgets.IsEmpty())
{
// For now, only supports single widget. Gallery system to be implemented to support
// showing multiple widgets and/or cycling node widgets.
SubmixNodeUserWidget = UserWidgets[0];
}
}
// Pass the owning submix and the user widgets to the graph node
return SNew(SSubmixGraphNode, this)
.SubmixBase(SubmixBase)
.SubmixNodeUserWidget(SubmixNodeUserWidget);
}
}
return nullptr;
}
#undef LOCTEXT_NAMESPACE