ComboInput/Source/ComboInputEditor/Private/ComboActionGraphSchema.cpp
2023-09-29 15:11:48 -04:00

438 lines
15 KiB
C++

// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
#include "ComboActionGraphSchema.h"
#include "ComboActionGraph.h"
//#include "FConnectionDrawingPolicy_ComboActionGraph.h"
#include "GraphEditorActions.h"
#include "Ed/EdComboActionGraphEdge.h"
#include "Ed/EdComboActionGraphNode.h"
#include "Framework/Commands/GenericCommands.h"
#include "Nodes/ComboActionGraphEdge.h"
#include "Nodes/ComboActionGraphNode.h"
#define LOCTEXT_NAMESPACE "ComboActionGraph"
int32 UComboActionGraphSchema::CurrentCacheRefreshID = 0;
UEdGraphNode *FAssetSchemaAction_ComboActionGraphSchema_NewNode::PerformAction(UEdGraph *ParentGraph, UEdGraphPin *FromPin, const FVector2D Location, bool bSelectNewNode)
{
UEdGraphNode *ResultNode = nullptr;
if (NodeTemplate != nullptr)
{
const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorNewNode", "Combo Action Graph Editor: New Node"));
ParentGraph->Modify();
if (FromPin != nullptr)
FromPin->Modify();
NodeTemplate->Rename(nullptr, ParentGraph);
ParentGraph->AddNode(NodeTemplate, true, bSelectNewNode);
NodeTemplate->CreateNewGuid();
NodeTemplate->PostPlacedNewNode();
NodeTemplate->AllocateDefaultPins();
NodeTemplate->AutowireNewNode(FromPin);
NodeTemplate->NodePosX = Location.X;
NodeTemplate->NodePosY = Location.Y;
NodeTemplate->ComboActionGraphNode->SetFlags(EObjectFlags::RF_Transactional);
NodeTemplate->SetFlags(EObjectFlags::RF_Transactional);
ResultNode = NodeTemplate;
}
return ResultNode;
}
void FAssetSchemaAction_ComboActionGraphSchema_NewNode::AddReferencedObjects(FReferenceCollector &Collector)
{
FEdGraphSchemaAction::AddReferencedObjects(Collector);
Collector.AddReferencedObject(NodeTemplate);
}
UEdGraphNode *FAssetSchemaAction_ComboActionGraphSchema_NewEdge::PerformAction(UEdGraph *ParentGraph, UEdGraphPin *FromPin, const FVector2D Location, bool bSelectNewNode)
{
UEdGraphNode *ResultNode = nullptr;
if (NodeTemplate != nullptr)
{
const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorNewEdge", "Combo Action Graph Editor: New Edge"));
ParentGraph->Modify();
if (FromPin != nullptr)
FromPin->Modify();
NodeTemplate->Rename(nullptr, ParentGraph);
ParentGraph->AddNode(NodeTemplate, true, bSelectNewNode);
NodeTemplate->CreateNewGuid();
NodeTemplate->PostPlacedNewNode();
NodeTemplate->AllocateDefaultPins();
NodeTemplate->AutowireNewNode(FromPin);
NodeTemplate->NodePosX = Location.X;
NodeTemplate->NodePosY = Location.Y;
NodeTemplate->ComboActionGraphEdge->SetFlags(EObjectFlags::RF_Transactional);
NodeTemplate->SetFlags(EObjectFlags::RF_Transactional);
ResultNode = NodeTemplate;
}
return ResultNode;
}
void FAssetSchemaAction_ComboActionGraphSchema_NewEdge::AddReferencedObjects(FReferenceCollector &Collector)
{
FEdGraphSchemaAction::AddReferencedObjects(Collector);
Collector.AddReferencedObject(NodeTemplate);
}
void UComboActionGraphSchema::GetBreakLinkToSubMenuActions(UToolMenu *Menu, UEdGraphPin *InGraphPin)
{
// Make sure we have a unique name for every entry in the list
TMap<FString, uint32> LinkTitleCount;
FToolMenuSection &Section = Menu->FindOrAddSection("ComboActionGraphAssetGraphSchemaPinActions");
// Add all the links we could break from
for (TArray<class UEdGraphPin*>::TConstIterator Links(InGraphPin->LinkedTo); Links; ++Links)
{
UEdGraphPin* Pin = *Links;
FString TitleString = Pin->GetOwningNode()->GetNodeTitle(ENodeTitleType::ListView).ToString();
FText Title = FText::FromString(TitleString);
if (Pin->PinName != TEXT(""))
{
TitleString = FString::Printf(TEXT("%s (%s)"), *TitleString, *Pin->PinName.ToString());
// Add name of connection if possible
FFormatNamedArguments Args;
Args.Add(TEXT("NodeTitle"), Title);
Args.Add(TEXT("PinName"), Pin->GetDisplayName());
Title = FText::Format(LOCTEXT("BreakDescPin", "{NodeTitle} ({PinName})"), Args);
}
uint32& Count = LinkTitleCount.FindOrAdd(TitleString);
FText Description;
FFormatNamedArguments Args;
Args.Add(TEXT("NodeTitle"), Title);
Args.Add(TEXT("NumberOfNodes"), Count);
if (Count == 0)
{
Description = FText::Format(LOCTEXT("BreakDesc", "Break link to {NodeTitle}"), Args);
}
else
{
Description = FText::Format(LOCTEXT("BreakDescMulti", "Break link to {NodeTitle} ({NumberOfNodes})"), Args);
}
++Count;
Section.AddMenuEntry(NAME_None, Description, Description, FSlateIcon(), FUIAction(
FExecuteAction::CreateUObject(this, &UComboActionGraphSchema::BreakSinglePinLink, const_cast<UEdGraphPin*>(InGraphPin), *Links)));
}
}
void UComboActionGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder &ContextMenuBuilder) const
{
UComboActionGraph *Graph = CastChecked<UComboActionGraph>(ContextMenuBuilder.CurrentGraph->GetOuter());
if (Graph->NodeType == nullptr)
{
return;
}
const bool bNoParent = (ContextMenuBuilder.FromPin == NULL);
FText AddToolTip = LOCTEXT("NewMoutneaDialogueGraphNodeTooltip", "Add Dialogue Node here");
TSet<TSubclassOf<UComboActionGraphNode>> Visited;
FText Desc = Graph->NodeType.GetDefaultObject()->ContextMenuName;
FText NodeCategory = Graph->NodeType.GetDefaultObject()->GetNodeCategory();
if (Desc.IsEmpty())
{
FString Title = Graph->NodeType->GetName();
Title.RemoveFromEnd("_C");
Desc = FText::FromString(Title);
}
if (!Graph->NodeType->HasAnyClassFlags(CLASS_Abstract))
{
TSharedPtr<FAssetSchemaAction_ComboActionGraphSchema_NewNode> NewNodeAction(new FAssetSchemaAction_ComboActionGraphSchema_NewNode(NodeCategory, Desc, AddToolTip, 0));
NewNodeAction->NodeTemplate = NewObject<UEdComboActionGraphNode>(ContextMenuBuilder.OwnerOfTemporaries);
NewNodeAction->NodeTemplate->ComboActionGraphNode = NewObject<UComboActionGraphNode>(NewNodeAction->NodeTemplate, Graph->NodeType);
NewNodeAction->NodeTemplate->ComboActionGraphNode->Graph = Graph;
ContextMenuBuilder.AddAction(NewNodeAction);
Visited.Add(Graph->NodeType);
}
for (TObjectIterator<UClass> It; It; ++It)
{
if (It->IsChildOf(Graph->NodeType) && !It->HasAnyClassFlags(CLASS_Abstract) && !Visited.Contains(*It))
{
TSubclassOf<UComboActionGraphNode> NodeType = *It;
if (It->GetName().StartsWith("REINST") || It->GetName().StartsWith("SKEL"))
continue;
if (!Graph->GetClass()->IsChildOf(NodeType.GetDefaultObject()->CompatibleGraphType))
continue;
if (!NodeType.GetDefaultObject()->bAllowManualCreate)
continue;
Desc = NodeType.GetDefaultObject()->ContextMenuName;
AddToolTip = NodeType.GetDefaultObject()->GetDescription();
NodeCategory = NodeType.GetDefaultObject()->GetNodeCategory();
if (Desc.IsEmpty())
{
FString Title = NodeType->GetName();
Title.RemoveFromEnd("_C");
Desc = FText::FromString(Title);
}
TSharedPtr<FAssetSchemaAction_ComboActionGraphSchema_NewNode> Action(new FAssetSchemaAction_ComboActionGraphSchema_NewNode(NodeCategory, Desc, AddToolTip, 0));
Action->NodeTemplate = NewObject<UEdComboActionGraphNode>(ContextMenuBuilder.OwnerOfTemporaries);
Action->NodeTemplate->ComboActionGraphNode = NewObject<UComboActionGraphNode>(Action->NodeTemplate, NodeType);
Action->NodeTemplate->ComboActionGraphNode->Graph = Graph;
ContextMenuBuilder.AddAction(Action);
Visited.Add(NodeType);
}
}
}
void UComboActionGraphSchema::GetContextMenuActions(UToolMenu *Menu, UGraphNodeContextMenuContext *Context) const
{
if (Context->Pin)
{
{
FToolMenuSection& Section = Menu->AddSection("ComboActionGraphAssetGraphSchemaNodeActions", LOCTEXT("PinActionsMenuHeader", "Pin Actions"));
// Only display the 'Break Links' option if there is a link to break!
if (Context->Pin->LinkedTo.Num() > 0)
{
Section.AddMenuEntry(FGraphEditorCommands::Get().BreakPinLinks);
// add sub menu for break link to
if (Context->Pin->LinkedTo.Num() > 1)
{
Section.AddSubMenu(
"BreakLinkTo",
LOCTEXT("BreakLinkTo", "Break Link To..."),
LOCTEXT("BreakSpecificLinks", "Break a specific link..."),
FNewToolMenuDelegate::CreateUObject((UComboActionGraphSchema *const)this, &UComboActionGraphSchema::GetBreakLinkToSubMenuActions, const_cast<UEdGraphPin*>(Context->Pin)));
}
else
{
((UComboActionGraphSchema *const)this)->GetBreakLinkToSubMenuActions(Menu, const_cast<UEdGraphPin*>(Context->Pin));
}
}
}
}
else if (Context->Node)
{
{
FToolMenuSection& Section = Menu->AddSection("ComboActionGraphAssetGraphSchemaNodeActions", LOCTEXT("ClassActionsMenuHeader", "Node Actions"));
Section.AddSeparator(FName(TEXT("Main Options")));
Section.AddMenuEntry(FGenericCommands::Get().Rename);
Section.AddSeparator(FName(TEXT("Other Options")));
Section.AddMenuEntry(FGenericCommands::Get().Delete);
Section.AddMenuEntry(FGenericCommands::Get().Cut);
Section.AddMenuEntry(FGenericCommands::Get().Copy);
Section.AddMenuEntry(FGenericCommands::Get().Duplicate);
Section.AddMenuEntry(FGraphEditorCommands::Get().BreakNodeLinks);
}
}
Super::GetContextMenuActions(Menu, Context);
}
const FPinConnectionResponse UComboActionGraphSchema::CanCreateConnection(const UEdGraphPin *A, const UEdGraphPin *B) const
{
// Make sure the pins are not on the same node
if (A->GetOwningNode() == B->GetOwningNode())
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorSameNode", "Both are on the same node"));
}
// Compare the directions
if ((A->Direction == EGPD_Input) && (B->Direction == EGPD_Input))
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorInput", "Can't connect input node to input node"));
}
else if ((A->Direction == EGPD_Output) && (B->Direction == EGPD_Output))
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorOutput", "Can't connect output node to output node"));
}
// check for cycles
FComboActionNodeVisitorCycleChecker CycleChecker;
if (!CycleChecker.CheckForLoop(A->GetOwningNode(), B->GetOwningNode()))
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorCycle", "Can't create a graph cycle"));
}
if (!CycleChecker.CheckForLoop(B->GetOwningNode(), A->GetOwningNode()))
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorCycle", "Can't create a graph cycle"));
}
UEdComboActionGraphNode* EdNode_A = Cast<UEdComboActionGraphNode>(A->GetOwningNode());
UEdComboActionGraphNode* EdNode_B = Cast<UEdComboActionGraphNode>(B->GetOwningNode());
if (EdNode_A == nullptr || EdNode_B == nullptr)
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinError", "Not a valid UComboActionGraphEdNode"));
}
FText ErrorMessage;
if (A->Direction == EGPD_Input)
{
if (!EdNode_A->ComboActionGraphNode->CanCreateConnection(EdNode_B->ComboActionGraphNode, EGPD_Input, ErrorMessage))
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, ErrorMessage);
}
}
else
{
if (!EdNode_B->ComboActionGraphNode->CanCreateConnection(EdNode_A->ComboActionGraphNode, EGPD_Output, ErrorMessage))
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, ErrorMessage);
}
}
if (EdNode_A->ComboActionGraphNode->GetGraph()->bEdgeEnabled)
{
return FPinConnectionResponse(CONNECT_RESPONSE_MAKE_WITH_CONVERSION_NODE, LOCTEXT("PinConnect", "Connect nodes with edge"));
}
else
{
return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, LOCTEXT("PinConnect", "Connect nodes"));
}
}
bool UComboActionGraphSchema::CreateAutomaticConversionNodeAndConnections(UEdGraphPin *A, UEdGraphPin *B) const
{
UEdComboActionGraphNode *NodeA = Cast<UEdComboActionGraphNode>(A->GetOwningNode());
UEdComboActionGraphNode *NodeB = Cast<UEdComboActionGraphNode>(B->GetOwningNode());
if (NodeA == nullptr || NodeB == nullptr)
return false;
if (NodeA->GetInputPin() == nullptr || NodeA->GetOutputPin() == nullptr || NodeB->GetInputPin() == nullptr || NodeB->GetOutputPin() == nullptr)
return false;
UComboActionGraph* Graph = NodeA->ComboActionGraphNode->GetGraph();
FVector2D InitPos((NodeA->NodePosX + NodeB->NodePosX) / 2, (NodeA->NodePosY + NodeB->NodePosY) / 2);
FAssetSchemaAction_ComboActionGraphSchema_NewEdge Action;
Action.NodeTemplate = NewObject<UEdComboActionGraphEdge>(NodeA->GetGraph());
Action.NodeTemplate->SetEdge(NewObject<UComboActionGraphEdge>(Action.NodeTemplate, Graph->EdgeType));
UEdComboActionGraphEdge *EdgeNode = Cast<UEdComboActionGraphEdge>(Action.PerformAction(NodeA->GetGraph(), nullptr, InitPos, false));
if (A->Direction == EEdGraphPinDirection::EGPD_Output)
{
EdgeNode->CreateConnections(NodeA, NodeB);
}
else
{
EdgeNode->CreateConnections(NodeB, NodeA);
}
return true;
}
FConnectionDrawingPolicy *UComboActionGraphSchema::CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj) const
{
/*
if (const UMounteaDialogueGraphEditorSettings* MounteaDialogueGraphEditorSettings = GetMutableDefault<UMounteaDialogueGraphEditorSettings>())
{
if (MounteaDialogueGraphEditorSettings->AllowAdvancedWiring())
{
return new FConnectionDrawingPolicy_AdvancedMounteaDialogueGraph(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements, InGraphObj);
}
}
*/
//return new FConnectionDrawingPolicy_ComboActionGraph(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements, InGraphObj);
return nullptr;
}
FLinearColor UComboActionGraphSchema::GetPinTypeColor(const FEdGraphPinType &PinType) const
{
return FColor::White;
}
void UComboActionGraphSchema::BreakNodeLinks(UEdGraphNode &TargetNode) const
{
const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakNodeLinks", "Break Node Links"));
Super::BreakNodeLinks(TargetNode);
}
void UComboActionGraphSchema::BreakPinLinks(UEdGraphPin &TargetPin, bool bSendsNodeNotifcation) const
{
const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakPinLinks", "Break Pin Links"));
Super::BreakPinLinks(TargetPin, bSendsNodeNotifcation);
}
void UComboActionGraphSchema::BreakSinglePinLink(UEdGraphPin *SourcePin, UEdGraphPin *TargetPin) const
{
const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakSinglePinLink", "Break Pin Link"));
Super::BreakSinglePinLink(SourcePin, TargetPin);
}
UEdGraphPin *UComboActionGraphSchema::DropPinOnNode(UEdGraphNode *InTargetNode, const FName &InSourcePinName, const FEdGraphPinType &InSourcePinType, EEdGraphPinDirection InSourcePinDirection) const
{
UEdComboActionGraphNode *EdNode = Cast<UEdComboActionGraphNode>(InTargetNode);
switch (InSourcePinDirection)
{
case EEdGraphPinDirection::EGPD_Input:
return EdNode->GetOutputPin();
case EEdGraphPinDirection::EGPD_Output:
return EdNode->GetInputPin();
default:
return nullptr;
}
}
bool UComboActionGraphSchema::SupportsDropPinOnNode(UEdGraphNode *InTargetNode, const FEdGraphPinType &InSourcePinType, EEdGraphPinDirection InSourcePinDirection, FText &OutErrorMessage) const
{
return Cast<UEdComboActionGraphNode>(InTargetNode) != nullptr;
}
bool UComboActionGraphSchema::IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const
{
return CurrentCacheRefreshID != InVisualizationCacheID;
}
int32 UComboActionGraphSchema::GetCurrentVisualizationCacheID() const
{
return CurrentCacheRefreshID;
}
void UComboActionGraphSchema::ForceVisualizationCacheClear() const
{
CurrentCacheRefreshID++;
}
void UComboActionGraphSchema::CreateDefaultNodesForGraph(UEdGraph &Graph) const
{
Super::CreateDefaultNodesForGraph(Graph);
}
#undef LOCTEXT_NAMESPACE