Files
UnrealEngine/Engine/Plugins/PCG/Source/PCGEditor/Private/PCGEditorGraphSchemaActions.cpp
2025-05-18 13:04:45 +08:00

841 lines
30 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PCGEditorGraphSchemaActions.h"
#include "PCGEditorCommon.h"
#include "PCGEditorGraph.h"
#include "PCGEditorGraphSchema.h"
#include "PCGEditorModule.h"
#include "Nodes/PCGEditorGraphNode.h"
#include "Nodes/PCGEditorGraphNodeComment.h"
#include "Nodes/PCGEditorGraphNodeReroute.h"
#include "PCGGraph.h"
#include "PCGNode.h"
#include "PCGSubgraph.h"
#include "Elements/IO/PCGLoadAssetElement.h"
#include "Elements/PCGExecuteBlueprint.h"
#include "Elements/PCGLoopElement.h"
#include "Elements/PCGReroute.h"
#include "Elements/PCGUserParameterGet.h"
#include "UObject/UObjectGlobals.h"
#include "GraphEditor.h"
#include "ScopedTransaction.h"
#include "Framework/Application/SlateApplication.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Layout/WidgetPath.h"
#define LOCTEXT_NAMESPACE "PCGEditorGraphSchemaActions"
UEdGraphNode* FPCGEditorGraphSchemaAction_NewNativeElement::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode)
{
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(ParentGraph);
if (!EditorGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid EditorGraph"));
return nullptr;
}
UPCGGraph* PCGGraph = EditorGraph->GetPCGGraph();
if (!PCGGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid PCGGraph"));
return nullptr;
}
// Important - do not reconstruct the editor graph node/pins midway through this function as this will invalidate FromPin.
const FPCGDeferNodeReconstructScope DisableReconstruct(FromPin);
// Ensure compilation cache is populated before changing graph, so we can compare before/after compiled tasks later to detect change.
PCGGraph->PrimeGraphCompilationCache();
const FScopedTransaction Transaction(*FPCGEditorCommon::ContextIdentifier, LOCTEXT("PCGEditorNewNativeElement", "PCG Editor: New Native Element"), nullptr);
EditorGraph->Modify();
UPCGSettings* DefaultNodeSettings = nullptr;
UPCGNode* NewPCGNode = PCGGraph->AddNodeOfType(SettingsClass, DefaultNodeSettings);
if (!NewPCGNode)
{
UE_LOG(LogPCGEditor, Error, TEXT("Failed to add a node of type %s"), *SettingsClass->GetName());
return nullptr;
}
if (DefaultNodeSettings)
{
DefaultNodeSettings->ApplyPreconfiguredSettings(PreconfiguredInfo);
NewPCGNode->UpdateAfterSettingsChangeDuringCreation();
}
PostCreation(NewPCGNode);
FGraphNodeCreator<UPCGEditorGraphNode> NodeCreator(*EditorGraph);
const TSubclassOf<UPCGEditorGraphNode> GraphNodeClass = TSubclassOf<UPCGEditorGraphNode>(UPCGEditorGraph::GetGraphNodeClassFromPCGSettings(DefaultNodeSettings));
UPCGEditorGraphNode* NewNode = NodeCreator.CreateUserInvokedNode(bSelectNewNode, GraphNodeClass);
NewNode->Construct(NewPCGNode);
NewNode->NodePosX = Location.X;
NewNode->NodePosY = Location.Y;
NodeCreator.Finalize();
NewPCGNode->PositionX = Location.X;
NewPCGNode->PositionY = Location.Y;
if (FromPin && ensure(FromPin->GetOwningNode()))
{
NewNode->AutowireNewNode(FromPin);
}
return NewNode;
}
FSlateBrush const* FPCGEditorGraphSchemaAction_NewNativeElement::GetPaletteIcon() const
{
// Get the icon from the descriptor if it exists. Should only need to do this once.
if (!PaletteIcon && PreconfiguredInfo.ActionIconDescriptor)
{
const IPCGActionIconDescriptorBase* IconDescriptor = PreconfiguredInfo.ActionIconDescriptor.Get();
const FSlateBrush* BaseBrush = nullptr;
FLinearColor TintColor = FLinearColor::White;
switch (IconDescriptor->GetActionIconDescriptorType())
{
case EPCGActionIconDescriptorType::Label:
{
const FPCGActionIconByLabelDescriptor* LabelDescriptor = static_cast<const FPCGActionIconByLabelDescriptor*>(IconDescriptor);
BaseBrush = FAppStyle::GetBrush(LabelDescriptor->BrushLabel);
TintColor = LabelDescriptor->Tint;
}
break;
case EPCGActionIconDescriptorType::Metadata:
{
const UPCGEditorGraphSchema* Schema = GetDefault<UPCGEditorGraphSchema>();
check(Schema);
const FPCGActionIconByMetadataDescriptor* MetadataDescriptor = static_cast<const FPCGActionIconByMetadataDescriptor*>(IconDescriptor);
BaseBrush = Schema->GetMetadataTypeSlateBrush(MetadataDescriptor->GetContainerType());
TintColor = Schema->GetMetadataTypeColor(MetadataDescriptor->GetMetadataType());
}
break;
default:
// Invalid descriptor type
checkNoEntry();
}
if (BaseBrush)
{
PaletteIcon = MakeShared<FSlateBrush>(*BaseBrush);
PaletteIcon->TintColor = TintColor;
}
}
return PaletteIcon ? PaletteIcon.Get() : nullptr;
}
FSlateBrush const* FPCGEditorGraphSchemaAction_NewGetParameterElement::GetPaletteIcon() const
{
if (!PaletteIcon)
{
const UPCGEditorGraphSchema* Schema = GetDefault<UPCGEditorGraphSchema>();
check(Schema);
if (const FSlateBrush* BaseBrush = Schema->GetPropertyBagTypeSlateBrush(!PropertyDesc.ContainerTypes.IsEmpty() ? PropertyDesc.ContainerTypes[0] : EPropertyBagContainerType::None))
{
PaletteIcon = MakeShared<FSlateBrush>(*BaseBrush);
PaletteIcon->TintColor = Schema->GetPropertyBagTypeColor(PropertyDesc);
}
}
return PaletteIcon ? PaletteIcon.Get() : nullptr;
}
void FPCGEditorGraphSchemaAction_NewGetParameterElement::PostCreation(UPCGNode* NewNode)
{
check(NewNode);
UPCGUserParameterGetSettings* Settings = CastChecked<UPCGUserParameterGetSettings>(NewNode->GetSettings());
Settings->PropertyGuid = PropertyDesc.ID;
Settings->PropertyName = PropertyDesc.Name;
NewNode->NodeTitle = Settings->PropertyName;
// We need to set the settings to update the pins.
NewNode->SetSettingsInterface(Settings);
}
UEdGraphNode* FPCGEditorGraphSchemaAction_NewSettingsElement::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode)
{
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(ParentGraph);
if (!EditorGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid EditorGraph"));
return nullptr;
}
UPCGGraph* PCGGraph = EditorGraph->GetPCGGraph();
if (!PCGGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid PCGGraph"));
return nullptr;
}
UPCGSettings* Settings = CastChecked<UPCGSettings>(SettingsObjectPath.TryLoad());
if (!Settings)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid settings"));
return nullptr;
}
// Important - do not reconstruct the editor graph node/pins midway through this function as this will invalidate FromPin.
const FPCGDeferNodeReconstructScope DisableReconstruct(FromPin);
// Ensure compilation cache is populated before changing graph, so we can compare before/after compiled tasks later to detect change.
PCGGraph->PrimeGraphCompilationCache();
bool bCreateInstance = false;
if(Behavior != EPCGEditorNewSettingsBehavior::Normal)
{
bCreateInstance = (Behavior == EPCGEditorNewSettingsBehavior::ForceInstance);
}
else
{
FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys();
bCreateInstance = ModifierKeys.IsAltDown();
}
return MakeSettingsNode(EditorGraph, FromPin, Settings, FDeprecateSlateVector2D(Location), bSelectNewNode, bCreateInstance);
}
void FPCGEditorGraphSchemaAction_NewSettingsElement::MakeSettingsNodesOrContextualMenu(const TSharedRef<class SWidget>& InPanel, const FVector2D& InScreenPosition, UEdGraph* InGraph, const TArray<FSoftObjectPath>& InSettingsPaths, const TArray<FVector2D>& InLocations, bool bInSelectNewNodes)
{
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(InGraph);
if (!EditorGraph)
{
return;
}
TArray<UPCGSettings*> Settings;
TArray<FVector2D> SettingsLocations;
check(InSettingsPaths.Num() == InLocations.Num());
for(int32 PathIndex = 0; PathIndex < InSettingsPaths.Num(); ++PathIndex)
{
const FSoftObjectPath& SettingsPath = InSettingsPaths[PathIndex];
if (UPCGSettings* LoadedSettings = Cast<UPCGSettings>(SettingsPath.TryLoad()))
{
Settings.Add(LoadedSettings);
SettingsLocations.Add(InLocations[PathIndex]);
}
}
FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys();
const bool bModifiedKeysActive = ModifierKeys.IsControlDown() || ModifierKeys.IsAltDown();
if (bModifiedKeysActive)
{
MakeSettingsNodes(EditorGraph, Settings, SettingsLocations, bInSelectNewNodes, ModifierKeys.IsAltDown());
}
else if(!Settings.IsEmpty())
{
FMenuBuilder MenuBuilder(true, nullptr);
const FText SettingsTextName = ((Settings.Num() == 1) ? FText::FromName(Settings[0]->GetFName()) : LOCTEXT("ManySettings", "Settings"));
MenuBuilder.BeginSection("SettingsDroppedOn", SettingsTextName);
MenuBuilder.AddMenuEntry(
FText::Format(LOCTEXT("CopySettings", "Copy {0}"), SettingsTextName),
FText::Format(LOCTEXT("CopySettingsToolTip", "Copy the settings asset {0}, and keeps no reference to the original\n(Ctrl-drop to automatically create a copy)"), SettingsTextName),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateStatic(&FPCGEditorGraphSchemaAction_NewSettingsElement::MakeSettingsNodes, EditorGraph, Settings, SettingsLocations, bInSelectNewNodes, false),
FCanExecuteAction()
)
);
MenuBuilder.AddMenuEntry(
FText::Format(LOCTEXT("InstanceSettings", "Instance {0}"), SettingsTextName),
FText::Format(LOCTEXT("InstanceSettingsToolTip", "Instance the settings asset {0}\n(Alt-drop to automatically create an instance)"), SettingsTextName),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateStatic(&FPCGEditorGraphSchemaAction_NewSettingsElement::MakeSettingsNodes, EditorGraph, Settings, SettingsLocations, bInSelectNewNodes, true),
FCanExecuteAction()
)
);
TSharedRef<SWidget> PanelWidget = InPanel;
// Show dialog to choose getter vs setter
FSlateApplication::Get().PushMenu(
PanelWidget,
FWidgetPath(),
MenuBuilder.MakeWidget(),
InScreenPosition,
FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)
);
MenuBuilder.EndSection();
}
}
void FPCGEditorGraphSchemaAction_NewSettingsElement::MakeSettingsNodes(UPCGEditorGraph* InEditorGraph, TArray<UPCGSettings*> InSettings, TArray<FVector2D> InSettingsLocations, bool bInSelectNewNodes, bool bInCreateInstances)
{
check(InSettings.Num() == InSettingsLocations.Num());
for (int32 SettingsIndex = 0; SettingsIndex < InSettings.Num(); ++SettingsIndex)
{
FPCGEditorGraphSchemaAction_NewSettingsElement::MakeSettingsNode(InEditorGraph, nullptr, InSettings[SettingsIndex], InSettingsLocations[SettingsIndex], bInSelectNewNodes, bInCreateInstances);
}
}
UEdGraphNode* FPCGEditorGraphSchemaAction_NewSettingsElement::MakeSettingsNode(UPCGEditorGraph* InEditorGraph, UEdGraphPin* InFromPin, UPCGSettings* InSettings, FVector2D InLocation, bool bInSelectNewNode, bool bInCreateInstance)
{
if (!InEditorGraph || !InSettings)
{
return nullptr;
}
UPCGGraph* PCGGraph = InEditorGraph->GetPCGGraph();
if (!PCGGraph)
{
return nullptr;
}
// Important - do not reconstruct the editor graph node/pins midway through this function as this will invalidate InFromPin.
const FPCGDeferNodeReconstructScope DisableReconstruct(InFromPin);
const FScopedTransaction Transaction(*FPCGEditorCommon::ContextIdentifier, LOCTEXT("PCGEditorNewSettingsElement", "PCG Editor: New Settings Element"), nullptr);
InEditorGraph->Modify();
UPCGNode* NewPCGNode = nullptr;
if (bInCreateInstance)
{
NewPCGNode = PCGGraph->AddNodeInstance(InSettings);
}
else
{
UPCGSettings* NewSettings = nullptr;
NewPCGNode = PCGGraph->AddNodeCopy(InSettings, NewSettings);
}
if (!NewPCGNode)
{
UE_LOG(LogPCGEditor, Error, TEXT("Unable to create node"));
return nullptr;
}
FGraphNodeCreator<UPCGEditorGraphNode> NodeCreator(*InEditorGraph);
UPCGEditorGraphNode* NewNode = NodeCreator.CreateUserInvokedNode(bInSelectNewNode);
NewNode->Construct(NewPCGNode);
NewNode->NodePosX = InLocation.X;
NewNode->NodePosY = InLocation.Y;
NodeCreator.Finalize();
NewPCGNode->PositionX = InLocation.X;
NewPCGNode->PositionY = InLocation.Y;
if (InFromPin)
{
NewNode->AutowireNewNode(InFromPin);
}
return NewNode;
}
UEdGraphNode* FPCGEditorGraphSchemaAction_NewLoadAssetElement::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode)
{
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(ParentGraph);
if (!EditorGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid EditorGraph"));
return nullptr;
}
UPCGGraph* PCGGraph = EditorGraph->GetPCGGraph();
if (!PCGGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid PCGGraph"));
return nullptr;
}
if(!Asset.GetSoftObjectPath().IsValid())
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid asset path."));
return nullptr;
}
// Important - do not reconstruct the editor graph node/pins midway through this function as this will invalidate FromPin.
const FPCGDeferNodeReconstructScope DisableReconstruct(FromPin);
// Ensure compilation cache is populated before changing graph, so we can compare before/after compiled tasks later to detect change.
PCGGraph->PrimeGraphCompilationCache();
const FScopedTransaction Transaction(*FPCGEditorCommon::ContextIdentifier, LOCTEXT("PCGEditorNewBlueprintELement", "PCG Editor: New Blueprint Element"), nullptr);
EditorGraph->Modify();
UPCGSettings* DefaultNodeSettings = nullptr;
UPCGNode* NewPCGNode = PCGGraph->AddNodeOfType(SettingsClass ? SettingsClass.Get() : UPCGLoadDataAssetSettings::StaticClass(), DefaultNodeSettings);
UPCGLoadDataAssetSettings* DefaultLoadAssetSettings = CastChecked<UPCGLoadDataAssetSettings>(DefaultNodeSettings);
DefaultLoadAssetSettings->SetFromAsset(Asset);
NewPCGNode->UpdateAfterSettingsChangeDuringCreation();
FGraphNodeCreator<UPCGEditorGraphNode> NodeCreator(*EditorGraph);
UPCGEditorGraphNode* NewNode = NodeCreator.CreateUserInvokedNode(bSelectNewNode);
NewNode->Construct(NewPCGNode);
NewNode->NodePosX = Location.X;
NewNode->NodePosY = Location.Y;
NodeCreator.Finalize();
NewPCGNode->PositionX = Location.X;
NewPCGNode->PositionY = Location.Y;
return NewNode;
}
UEdGraphNode* FPCGEditorGraphSchemaAction_NewBlueprintElement::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode)
{
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(ParentGraph);
if (!EditorGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid EditorGraph"));
return nullptr;
}
UPCGGraph* PCGGraph = EditorGraph->GetPCGGraph();
if (!PCGGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid PCGGraph"));
return nullptr;
}
// Important - do not reconstruct the editor graph node/pins midway through this function as this will invalidate FromPin.
const FPCGDeferNodeReconstructScope DisableReconstruct(FromPin);
// Ensure compilation cache is populated before changing graph, so we can compare before/after compiled tasks later to detect change.
PCGGraph->PrimeGraphCompilationCache();
const FScopedTransaction Transaction(*FPCGEditorCommon::ContextIdentifier, LOCTEXT("PCGEditorNewBlueprintELement", "PCG Editor: New Blueprint Element"), nullptr);
EditorGraph->Modify();
UPCGSettings* DefaultNodeSettings = nullptr;
UPCGNode* NewPCGNode = PCGGraph->AddNodeOfType(UPCGBlueprintSettings::StaticClass(), DefaultNodeSettings);
UPCGBlueprintSettings* DefaultBlueprintSettings = CastChecked<UPCGBlueprintSettings>(DefaultNodeSettings);
UPCGBlueprintElement* ElementInstance = nullptr;
TSubclassOf<UPCGBlueprintElement> BlueprintClass = BlueprintClassPath.TryLoadClass<UPCGBlueprintElement>();
DefaultBlueprintSettings->SetElementType(BlueprintClass, ElementInstance);
DefaultBlueprintSettings->ApplyPreconfiguredSettings(PreconfiguredInfo);
NewPCGNode->UpdateAfterSettingsChangeDuringCreation();
FGraphNodeCreator<UPCGEditorGraphNode> NodeCreator(*EditorGraph);
UPCGEditorGraphNode* NewNode = NodeCreator.CreateUserInvokedNode(bSelectNewNode);
NewNode->Construct(NewPCGNode);
NewNode->NodePosX = Location.X;
NewNode->NodePosY = Location.Y;
NodeCreator.Finalize();
NewPCGNode->PositionX = Location.X;
NewPCGNode->PositionY = Location.Y;
if (FromPin)
{
NewNode->AutowireNewNode(FromPin);
}
return NewNode;
}
UEdGraphNode* FPCGEditorGraphSchemaAction_NewSubgraphElement::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode)
{
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(ParentGraph);
if (!EditorGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid EditorGraph"));
return nullptr;
}
UPCGGraph* PCGGraph = EditorGraph->GetPCGGraph();
if (!PCGGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid PCGGraph"));
return nullptr;
}
UPCGGraphInterface* Graph = CastChecked<UPCGGraphInterface>(SubgraphObjectPath.TryLoad());
if (!Graph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Could not load Graph"));
return nullptr;
}
// Important - do not reconstruct the editor graph node/pins midway through this function as this will invalidate FromPin.
const FPCGDeferNodeReconstructScope DisableReconstruct(FromPin);
// Ensure compilation cache is populated before changing graph, so we can compare before/after compiled tasks later to detect change.
PCGGraph->PrimeGraphCompilationCache();
bool bCreateLoop = false;
if (Behavior != EPCGEditorNewPCGGraphBehavior::Normal)
{
bCreateLoop = (Behavior == EPCGEditorNewPCGGraphBehavior::LoopNode);
}
else
{
FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys();
bCreateLoop = ModifierKeys.IsAltDown();
}
return MakeGraphNode(EditorGraph, FromPin, Graph, FDeprecateSlateVector2D(Location), bSelectNewNode, bCreateLoop);
}
void FPCGEditorGraphSchemaAction_NewSubgraphElement::MakeGraphNodesOrContextualMenu(const TSharedRef<class SWidget>& InPanel, const FVector2D& InScreenPosition, UEdGraph* InGraph, const TArray<FSoftObjectPath>& InGraphPaths, const TArray<FVector2D>& InLocations, bool bInSelectNewNodes)
{
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(InGraph);
if (!EditorGraph)
{
return;
}
TArray<UPCGGraphInterface*> Graph;
TArray<FVector2D> GraphLocations;
check(InGraphPaths.Num() == InLocations.Num());
for (int32 PathIndex = 0; PathIndex < InGraphPaths.Num(); ++PathIndex)
{
const FSoftObjectPath& GraphPath = InGraphPaths[PathIndex];
if (UPCGGraphInterface* LoadedGraph = Cast<UPCGGraphInterface>(GraphPath.TryLoad()))
{
Graph.Add(LoadedGraph);
GraphLocations.Add(InLocations[PathIndex]);
}
}
FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys();
const bool bModifiedKeysActive = ModifierKeys.IsControlDown() || ModifierKeys.IsAltDown();
if (bModifiedKeysActive)
{
MakeGraphNodes(EditorGraph, Graph, GraphLocations, bInSelectNewNodes, ModifierKeys.IsAltDown());
}
else if (!Graph.IsEmpty())
{
FMenuBuilder MenuBuilder(true, nullptr);
const FText GraphTextName = ((Graph.Num() == 1) ? FText::FromName(Graph[0]->GetFName()) : LOCTEXT("MultipleGraphs", "Multiple Graphs"));
MenuBuilder.BeginSection("GraphDroppedOn", GraphTextName);
MenuBuilder.AddMenuEntry(
FText::Format(LOCTEXT("CreateSubgraphNode", "Create {0} Subgraph Node"), GraphTextName),
FText::Format(LOCTEXT("CreateSubgraphToolTip", "Creates a subgraph for Graph asset {0} \n(Ctrl-drop to automatically create a subgraph node)"), GraphTextName),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateStatic(&FPCGEditorGraphSchemaAction_NewSubgraphElement::MakeGraphNodes, EditorGraph, Graph, GraphLocations, bInSelectNewNodes, false)
)
);
MenuBuilder.AddMenuEntry(
FText::Format(LOCTEXT("CreateLoopNode", "Create {0} Loop Node"), GraphTextName),
FText::Format(LOCTEXT("CreateLoopNodeToolTip", "Creates a loop node for Graph asset {0}\n(Alt-drop to automatically create a loop node)"), GraphTextName),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateStatic(&FPCGEditorGraphSchemaAction_NewSubgraphElement::MakeGraphNodes, EditorGraph, Graph, GraphLocations, bInSelectNewNodes, true)
)
);
TSharedRef<SWidget> PanelWidget = InPanel;
// Show dialog to choose getter vs setter
FSlateApplication::Get().PushMenu(
PanelWidget,
FWidgetPath(),
MenuBuilder.MakeWidget(),
InScreenPosition,
FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)
);
MenuBuilder.EndSection();
}
}
void FPCGEditorGraphSchemaAction_NewSubgraphElement::MakeGraphNodes(UPCGEditorGraph* InEditorGraph, TArray<UPCGGraphInterface*> InGraph, TArray<FVector2D> InGraphLocations, bool bInSelectNewNodes, bool bInCreateLoop)
{
check(InGraph.Num() == InGraphLocations.Num());
for (int32 GraphIndex = 0; GraphIndex < InGraph.Num(); ++GraphIndex)
{
FPCGEditorGraphSchemaAction_NewSubgraphElement::MakeGraphNode(InEditorGraph, nullptr, InGraph[GraphIndex], InGraphLocations[GraphIndex], bInSelectNewNodes, bInCreateLoop);
}
}
UEdGraphNode* FPCGEditorGraphSchemaAction_NewSubgraphElement::MakeGraphNode(UPCGEditorGraph* InEditorGraph, UEdGraphPin* InFromPin, UPCGGraphInterface* InGraph, const FVector2D& InLocation, bool bInSelectNewNode, bool bInCreateLoop)
{
if (!InEditorGraph || !InGraph)
{
return nullptr;
}
UPCGGraph* PCGGraph = InEditorGraph->GetPCGGraph();
if (!PCGGraph)
{
return nullptr;
}
// Important - do not reconstruct the editor graph node/pins midway through this function as this will invalidate InFromPin.
const FPCGDeferNodeReconstructScope DisableReconstruct(InFromPin);
const FScopedTransaction Transaction(*FPCGEditorCommon::ContextIdentifier, LOCTEXT("PCGEditorNewSubgraphElement", "PCG Editor: New Subgraph Element"), nullptr);
InEditorGraph->Modify();
UPCGSettings* DefaultNodeSettings = nullptr;
UPCGNode* NewPCGNode = nullptr;
if (!bInCreateLoop)
{
NewPCGNode = PCGGraph->AddNodeOfType(UPCGSubgraphSettings::StaticClass(), DefaultNodeSettings);
UPCGSubgraphSettings* DefaultSubgraphSettings = CastChecked<UPCGSubgraphSettings>(DefaultNodeSettings);
DefaultSubgraphSettings->SetSubgraph(InGraph);
}
else
{
NewPCGNode = PCGGraph->AddNodeOfType(UPCGLoopSettings::StaticClass(), DefaultNodeSettings);
UPCGLoopSettings* DefaultLoopSettings = CastChecked<UPCGLoopSettings>(DefaultNodeSettings);
DefaultLoopSettings->SetSubgraph(InGraph);
}
if (!NewPCGNode)
{
UE_LOG(LogPCGEditor, Error, TEXT("Unable to create node"));
return nullptr;
}
NewPCGNode->UpdateAfterSettingsChangeDuringCreation();
FGraphNodeCreator<UPCGEditorGraphNode> NodeCreator(*InEditorGraph);
UPCGEditorGraphNode* NewNode = NodeCreator.CreateUserInvokedNode(bInSelectNewNode);
NewNode->Construct(NewPCGNode);
NewNode->NodePosX = InLocation.X;
NewNode->NodePosY = InLocation.Y;
NodeCreator.Finalize();
NewPCGNode->PositionX = InLocation.X;
NewPCGNode->PositionY = InLocation.Y;
if (InFromPin)
{
NewNode->AutowireNewNode(InFromPin);
}
return NewNode;
}
UEdGraphNode* FPCGEditorGraphSchemaAction_NewComment::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode /*= true*/)
{
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(ParentGraph);
if (!EditorGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid EditorGraph"));
return nullptr;
}
UPCGGraph* PCGGraph = EditorGraph->GetPCGGraph();
if (!PCGGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid PCGGraph"));
return nullptr;
}
UPCGEditorGraphNodeComment* CommentTemplate = NewObject<UPCGEditorGraphNodeComment>();
TSharedPtr<SGraphEditor> GraphEditorPtr = SGraphEditor::FindGraphEditorForGraph(ParentGraph);
FVector2f SpawnLocation = Location;
FSlateRect Bounds;
if (GraphEditorPtr && GraphEditorPtr->GetBoundsForSelectedNodes(Bounds, 50.0f))
{
CommentTemplate->SetBounds(Bounds);
SpawnLocation.X = CommentTemplate->NodePosX;
SpawnLocation.Y = CommentTemplate->NodePosY;
}
const FScopedTransaction Transaction(*FPCGEditorCommon::ContextIdentifier, LOCTEXT("PCGEditorGraphSchemaAction_NewComment", "PCG Editor: New Comment"), nullptr);
EditorGraph->Modify();
PCGGraph->Modify();
UPCGEditorGraphNodeComment* NewNode = FEdGraphSchemaAction_NewNode::SpawnNodeFromTemplate<UPCGEditorGraphNodeComment>(ParentGraph, CommentTemplate, SpawnLocation, bSelectNewNode);
return NewNode;
}
UEdGraphNode* FPCGEditorGraphSchemaAction_NewReroute::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode)
{
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(ParentGraph);
if (!EditorGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid EditorGraph"));
return nullptr;
}
UPCGGraph* PCGGraph = EditorGraph->GetPCGGraph();
if (!PCGGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid PCGGraph"));
return nullptr;
}
// Important - do not reconstruct the editor graph node/pins midway through this function as this will invalidate FromPin.
const FPCGDeferNodeReconstructScope DisableReconstruct(FromPin);
// Ensure compilation cache is populated before changing graph, so we can compare before/after compiled tasks later to detect change.
PCGGraph->PrimeGraphCompilationCache();
const FScopedTransaction Transaction(*FPCGEditorCommon::ContextIdentifier, LOCTEXT("PCGEditorNewReroute", "PCG Editor: New Reroute Node"), nullptr);
EditorGraph->Modify();
UPCGSettings* DefaultNodeSettings = nullptr;
UPCGNode* NewPCGNode = PCGGraph->AddNodeOfType(UPCGRerouteSettings::StaticClass(), DefaultNodeSettings);
FGraphNodeCreator<UPCGEditorGraphNodeReroute> NodeCreator(*EditorGraph);
UPCGEditorGraphNodeReroute* NewNode = NodeCreator.CreateUserInvokedNode(bSelectNewNode);
NewNode->Construct(NewPCGNode);
NewNode->NodePosX = Location.X;
NewNode->NodePosY = Location.Y;
NodeCreator.Finalize();
NewPCGNode->PositionX = Location.X;
NewPCGNode->PositionY = Location.Y;
if (FromPin)
{
NewNode->AutowireNewNode(FromPin);
}
return NewNode;
}
UEdGraphNode* FPCGEditorGraphSchemaAction_NewNamedRerouteUsage::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode)
{
check(DeclarationNode);
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(ParentGraph);
if (!EditorGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid EditorGraph"));
return nullptr;
}
UPCGGraph* PCGGraph = EditorGraph->GetPCGGraph();
if (!PCGGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid PCGGraph"));
return nullptr;
}
const FScopedTransaction Transaction(*FPCGEditorCommon::ContextIdentifier, LOCTEXT("PCGEditorNewNamedRerouteUsage", "PCG Editor: New Named Reroute Node Usage"), nullptr);
EditorGraph->Modify();
// Ensure compilation cache is populated before changing graph, so we can compare before/after compiled tasks later to detect change.
PCGGraph->PrimeGraphCompilationCache();
PCGGraph->DisableNotificationsForEditor();
ON_SCOPE_EXIT
{
PCGGraph->EnableNotificationsForEditor();
};
UPCGNamedRerouteDeclarationSettings* Declaration = CastChecked<UPCGNamedRerouteDeclarationSettings>(DeclarationNode->GetSettings());
UPCGSettings* DefaultNodeSettings = nullptr;
UPCGNode* NewPCGNode = PCGGraph->AddNodeOfType(UPCGNamedRerouteUsageSettings::StaticClass(), DefaultNodeSettings);
if (!NewPCGNode || !DefaultNodeSettings)
{
UE_LOG(LogPCGEditor, Error, TEXT("Failed creating named reroute node."));
return nullptr;
}
NewPCGNode->UpdateAfterSettingsChangeDuringCreation();
CastChecked<UPCGNamedRerouteUsageSettings>(DefaultNodeSettings)->Declaration = Declaration;
NewPCGNode->NodeTitle = DeclarationNode->NodeTitle;
NewPCGNode->NodeTitleColor = DeclarationNode->NodeTitleColor;
// Create edge from the declaration to the new node
PCGGraph->AddEdge(const_cast<UPCGNode*>(DeclarationNode.Get()), PCGNamedRerouteConstants::InvisiblePinLabel, NewPCGNode, PCGPinConstants::DefaultInputLabel);
FGraphNodeCreator<UPCGEditorGraphNodeNamedRerouteUsage> NodeCreator(*EditorGraph);
UPCGEditorGraphNodeNamedRerouteUsage* NewNode = NodeCreator.CreateUserInvokedNode(bSelectNewNode);
NewNode->Construct(NewPCGNode);
NewNode->NodePosX = Location.X;
NewNode->NodePosY = Location.Y;
NodeCreator.Finalize();
NewPCGNode->PositionX = Location.X;
NewPCGNode->PositionY = Location.Y;
return NewNode;
}
UEdGraphNode* FPCGEditorGraphSchemaAction_NewNamedRerouteDeclaration::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode)
{
UPCGEditorGraph* EditorGraph = Cast<UPCGEditorGraph>(ParentGraph);
if (!EditorGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid EditorGraph"));
return nullptr;
}
UPCGGraph* PCGGraph = EditorGraph->GetPCGGraph();
if (!PCGGraph)
{
UE_LOG(LogPCGEditor, Error, TEXT("Invalid PCGGraph"));
return nullptr;
}
// Important - do not reconstruct the editor graph node/pins midway through this function as this will invalidate FromPin.
const FPCGDeferNodeReconstructScope DisableReconstruct(FromPin);
// Ensure compilation cache is populated before changing graph, so we can compare before/after compiled tasks later to detect change.
PCGGraph->PrimeGraphCompilationCache();
const FScopedTransaction Transaction(*FPCGEditorCommon::ContextIdentifier, LOCTEXT("PCGEditorNewNamedRerouteDeclaration", "PCG Editor: New Named Reroute Node Declaration"), nullptr);
EditorGraph->Modify();
UPCGSettings* DefaultNodeSettings = nullptr;
UPCGNode* NewPCGNode = PCGGraph->AddNodeOfType(UPCGNamedRerouteDeclarationSettings::StaticClass(), DefaultNodeSettings);
if (!NewPCGNode || !DefaultNodeSettings)
{
UE_LOG(LogPCGEditor, Error, TEXT("Failed creating named reroute node."));
return nullptr;
}
NewPCGNode->UpdateAfterSettingsChangeDuringCreation();
FGraphNodeCreator<UPCGEditorGraphNodeNamedRerouteDeclaration> NodeCreator(*EditorGraph);
UPCGEditorGraphNodeNamedRerouteDeclaration* NewNode = NodeCreator.CreateUserInvokedNode(bSelectNewNode);
NewNode->Construct(NewPCGNode);
NewNode->NodePosX = Location.X;
NewNode->NodePosY = Location.Y;
NodeCreator.Finalize();
NewPCGNode->PositionX = Location.X;
NewPCGNode->PositionY = Location.Y;
UPCGNode* FromNode = nullptr;
if (FromPin)
{
FromNode = CastChecked<UPCGEditorGraphNodeBase>(FromPin->GetOwningNode())->GetPCGNode();
NewNode->AutowireNewNode(FromPin);
}
const FString NewName = NewNode->GenerateNodeName(FromNode, FromPin ? FromPin->PinName : NAME_None);
NewNode->OnRenameNode(NewName);
return NewNode;
}
#undef LOCTEXT_NAMESPACE