Files
UnrealEngineUWP/Engine/Source/Editor/AIGraph/Private/AIGraphSchema.cpp
Lukasz Furman 25b22b990a fixed order of behavior tree nodes when creating a new node from existing pin
#ue4 UE-17114

[CL 2593361 by Lukasz Furman in Main branch]
2015-06-19 09:18:09 -04:00

295 lines
10 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "AIGraphPrivatePCH.h"
#include "BlueprintGraphDefinitions.h"
#include "GraphEditorActions.h"
#include "AIGraphConnectionDrawingPolicy.h"
#include "ScopedTransaction.h"
#include "SGraphEditorImpl.h"
#include "GenericCommands.h"
#define LOCTEXT_NAMESPACE "AIGraph"
#define SNAP_GRID (16) // @todo ensure this is the same as SNodePanel::GetSnapGridSize()
namespace
{
// Maximum distance a drag can be off a node edge to require 'push off' from node
const int32 NodeDistance = 60;
}
UEdGraphNode* FAISchemaAction_NewNode::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode)
{
UEdGraphNode* ResultNode = NULL;
// If there is a template, we actually use it
if (NodeTemplate != NULL)
{
const FScopedTransaction Transaction(LOCTEXT("AddNode", "Add Node"));
ParentGraph->Modify();
if (FromPin)
{
FromPin->Modify();
}
NodeTemplate->SetFlags(RF_Transactional);
// set outer to be the graph so it doesn't go away
NodeTemplate->Rename(NULL, ParentGraph, REN_NonTransactional);
ParentGraph->AddNode(NodeTemplate, true);
NodeTemplate->CreateNewGuid();
NodeTemplate->PostPlacedNewNode();
// For input pins, new node will generally overlap node being dragged off
// Work out if we want to visually push away from connected node
int32 XLocation = Location.X;
if (FromPin && FromPin->Direction == EGPD_Input)
{
UEdGraphNode* PinNode = FromPin->GetOwningNode();
const float XDelta = FMath::Abs(PinNode->NodePosX - Location.X);
if (XDelta < NodeDistance)
{
// Set location to edge of current node minus the max move distance
// to force node to push off from connect node enough to give selection handle
XLocation = PinNode->NodePosX - NodeDistance;
}
}
NodeTemplate->NodePosX = XLocation;
NodeTemplate->NodePosY = Location.Y;
NodeTemplate->SnapToGrid(SNAP_GRID);
// setup pins after placing node in correct spot, since pin sorting will happen as soon as link connection change occurs
NodeTemplate->AllocateDefaultPins();
NodeTemplate->AutowireNewNode(FromPin);
ResultNode = NodeTemplate;
}
return ResultNode;
}
UEdGraphNode* FAISchemaAction_NewNode::PerformAction(class UEdGraph* ParentGraph, TArray<UEdGraphPin*>& FromPins, const FVector2D Location, bool bSelectNewNode)
{
UEdGraphNode* ResultNode = NULL;
if (FromPins.Num() > 0)
{
ResultNode = PerformAction(ParentGraph, FromPins[0], Location);
// Try autowiring the rest of the pins
for (int32 Index = 1; Index < FromPins.Num(); ++Index)
{
ResultNode->AutowireNewNode(FromPins[Index]);
}
}
else
{
ResultNode = PerformAction(ParentGraph, NULL, Location, bSelectNewNode);
}
return ResultNode;
}
void FAISchemaAction_NewNode::AddReferencedObjects(FReferenceCollector& Collector)
{
FEdGraphSchemaAction::AddReferencedObjects(Collector);
// These don't get saved to disk, but we want to make sure the objects don't get GC'd while the action array is around
Collector.AddReferencedObject(NodeTemplate);
}
UEdGraphNode* FAISchemaAction_NewSubNode::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode)
{
ParentNode->AddSubNode(NodeTemplate, ParentGraph);
return NULL;
}
UEdGraphNode* FAISchemaAction_NewSubNode::PerformAction(class UEdGraph* ParentGraph, TArray<UEdGraphPin*>& FromPins, const FVector2D Location, bool bSelectNewNode)
{
return PerformAction(ParentGraph, NULL, Location, bSelectNewNode);
}
void FAISchemaAction_NewSubNode::AddReferencedObjects(FReferenceCollector& Collector)
{
FEdGraphSchemaAction::AddReferencedObjects(Collector);
// These don't get saved to disk, but we want to make sure the objects don't get GC'd while the action array is around
Collector.AddReferencedObject(NodeTemplate);
}
//////////////////////////////////////////////////////////////////////////
UAIGraphSchema::UAIGraphSchema(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
}
TSharedPtr<FAISchemaAction_NewNode> UAIGraphSchema::AddNewNodeAction(FGraphActionListBuilderBase& ContextMenuBuilder, const FText& Category, const FText& MenuDesc, const FString& Tooltip)
{
TSharedPtr<FAISchemaAction_NewNode> NewAction = TSharedPtr<FAISchemaAction_NewNode>(new FAISchemaAction_NewNode(Category, MenuDesc, Tooltip, 0));
ContextMenuBuilder.AddAction(NewAction);
return NewAction;
}
TSharedPtr<FAISchemaAction_NewSubNode> UAIGraphSchema::AddNewSubNodeAction(FGraphActionListBuilderBase& ContextMenuBuilder, const FText& Category, const FText& MenuDesc, const FString& Tooltip)
{
TSharedPtr<FAISchemaAction_NewSubNode> NewAction = TSharedPtr<FAISchemaAction_NewSubNode>(new FAISchemaAction_NewSubNode(Category, MenuDesc, Tooltip, 0));
ContextMenuBuilder.AddAction(NewAction);
return NewAction;
}
void UAIGraphSchema::GetSubNodeClasses(int32 SubNodeFlags, TArray<FGraphNodeClassData>& ClassData, UClass*& GraphNodeClass) const
{
// empty in base class
}
void UAIGraphSchema::GetGraphNodeContextActions(FGraphContextMenuBuilder& ContextMenuBuilder, int32 SubNodeFlags) const
{
UEdGraph* Graph = (UEdGraph*)ContextMenuBuilder.CurrentGraph;
UClass* GraphNodeClass = nullptr;
TArray<FGraphNodeClassData> NodeClasses;
GetSubNodeClasses(SubNodeFlags, NodeClasses, GraphNodeClass);
if (GraphNodeClass)
{
for (const auto& NodeClass : NodeClasses)
{
const FText NodeTypeName = FText::FromString(FName::NameToDisplayString(NodeClass.ToString(), false));
UAIGraphNode* OpNode = NewObject<UAIGraphNode>(Graph, GraphNodeClass);
OpNode->ClassData = NodeClass;
TSharedPtr<FAISchemaAction_NewSubNode> AddOpAction = UAIGraphSchema::AddNewSubNodeAction(ContextMenuBuilder, NodeClass.GetCategory(), NodeTypeName, "");
AddOpAction->ParentNode = Cast<UAIGraphNode>(ContextMenuBuilder.SelectedObjects[0]);
AddOpAction->NodeTemplate = OpNode;
}
}
}
void UAIGraphSchema::GetContextMenuActions(const UEdGraph* CurrentGraph, const UEdGraphNode* InGraphNode, const UEdGraphPin* InGraphPin, class FMenuBuilder* MenuBuilder, bool bIsDebugging) const
{
if (InGraphPin)
{
MenuBuilder->BeginSection("AIGraphSchemaPinActions", LOCTEXT("PinActionsMenuHeader", "Pin Actions"));
{
// Only display the 'Break Links' option if there is a link to break!
if (InGraphPin->LinkedTo.Num() > 0)
{
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().BreakPinLinks);
// add sub menu for break link to
if (InGraphPin->LinkedTo.Num() > 1)
{
MenuBuilder->AddSubMenu(
LOCTEXT("BreakLinkTo", "Break Link To..."),
LOCTEXT("BreakSpecificLinks", "Break a specific link..."),
FNewMenuDelegate::CreateUObject((UAIGraphSchema*const)this, &UAIGraphSchema::GetBreakLinkToSubMenuActions, const_cast<UEdGraphPin*>(InGraphPin)));
}
else
{
((UAIGraphSchema*const)this)->GetBreakLinkToSubMenuActions(*MenuBuilder, const_cast<UEdGraphPin*>(InGraphPin));
}
}
}
MenuBuilder->EndSection();
}
else if (InGraphNode)
{
MenuBuilder->BeginSection("BehaviorTreeGraphSchemaNodeActions", LOCTEXT("ClassActionsMenuHeader", "Node Actions"));
{
MenuBuilder->AddMenuEntry(FGenericCommands::Get().Delete);
MenuBuilder->AddMenuEntry(FGenericCommands::Get().Cut);
MenuBuilder->AddMenuEntry(FGenericCommands::Get().Copy);
MenuBuilder->AddMenuEntry(FGenericCommands::Get().Duplicate);
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().BreakNodeLinks);
}
MenuBuilder->EndSection();
}
Super::GetContextMenuActions(CurrentGraph, InGraphNode, InGraphPin, MenuBuilder, bIsDebugging);
}
void UAIGraphSchema::GetBreakLinkToSubMenuActions(class FMenuBuilder& MenuBuilder, UEdGraphPin* InGraphPin)
{
// Make sure we have a unique name for every entry in the list
TMap< FString, uint32 > LinkTitleCount;
// 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);
// 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;
MenuBuilder.AddMenuEntry(Description, Description, FSlateIcon(), FUIAction(
FExecuteAction::CreateUObject(this, &UAIGraphSchema::BreakSinglePinLink, const_cast< UEdGraphPin* >(InGraphPin), *Links)));
}
}
void UAIGraphSchema::BreakNodeLinks(UEdGraphNode& TargetNode) const
{
const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakNodeLinks", "Break Node Links"));
Super::BreakNodeLinks(TargetNode);
}
void UAIGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotification) const
{
const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakPinLinks", "Break Pin Links"));
Super::BreakPinLinks(TargetPin, bSendsNodeNotification);
}
void UAIGraphSchema::BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin)
{
const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakSinglePinLink", "Break Pin Link"));
Super::BreakSinglePinLink(SourcePin, TargetPin);
}
FLinearColor UAIGraphSchema::GetPinTypeColor(const FEdGraphPinType& PinType) const
{
return FColor::White;
}
bool UAIGraphSchema::ShouldHidePinDefaultValue(UEdGraphPin* Pin) const
{
check(Pin != NULL);
return Pin->bDefaultValueIsIgnored;
}
class FConnectionDrawingPolicy* UAIGraphSchema::CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const
{
return new FAIGraphConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements, InGraphObj);
}
#undef LOCTEXT_NAMESPACE