189 lines
6.6 KiB
C++
189 lines
6.6 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "MVVMConversionFunctionGraphSchema.h"
|
|
#include "Bindings/MVVMConversionFunctionHelper.h"
|
|
#include "EdGraphSchema_K2_Actions.h"
|
|
#include "EdGraph/EdGraphNode.h"
|
|
#include "EdGraph/EdGraphPin.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "K2Node_DynamicCast.h"
|
|
|
|
|
|
bool UMVVMConversionFunctionGraphSchema::TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const
|
|
{
|
|
bool bResult = Super::TryCreateConnection(A, B);
|
|
|
|
if (bResult && !A->LinkedTo.Contains(B))
|
|
{
|
|
// ConversionNode or Promotion node was created between the 2 graph pins
|
|
if (A->LinkedTo.Num() == 1)
|
|
{
|
|
if (UEdGraphNode* AutogeneratedNode = A->LinkedTo[0]->GetOwningNode())
|
|
{
|
|
for (UEdGraphPin* AutogeneratedPin : AutogeneratedNode->Pins)
|
|
{
|
|
if (AutogeneratedPin->LinkedTo.Contains(B))
|
|
{
|
|
UE::MVVM::ConversionFunctionHelper::MarkNodeAsAutoPromote(AutogeneratedNode);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
const FPinConnectionResponse UMVVMConversionFunctionGraphSchema::CanCreateConnection(const UEdGraphPin* PinA, const UEdGraphPin* PinB) const
|
|
{
|
|
check(PinA);
|
|
check(PinB);
|
|
if (PinA->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject && PinB->PinType.PinCategory == UEdGraphSchema_K2::PC_Object)
|
|
{
|
|
FPinConnectionResponse Response(CONNECT_RESPONSE_DISALLOW, FString::Printf(TEXT("Conversion disallowed from %s to %s"), *UEdGraphSchema_K2::PC_SoftObject.ToString(), *UEdGraphSchema_K2::PC_Object.ToString()));
|
|
Response.SetFatal();
|
|
return Response;
|
|
}
|
|
|
|
return Super::CanCreateConnection(PinA, PinB);
|
|
}
|
|
|
|
bool UMVVMAsyncConversionFunctionGraphSchema::CreateAutomaticConversionNodeAndConnections(UEdGraphPin* PinA, UEdGraphPin* PinB) const
|
|
{
|
|
// Determine which pin is an input and which pin is an output
|
|
UEdGraphPin* InputPin = nullptr;
|
|
UEdGraphPin* OutputPin = nullptr;
|
|
if (!CategorizePinsByDirection(PinA, PinB, /*out*/ InputPin, /*out*/ OutputPin))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
check(InputPin);
|
|
check(OutputPin);
|
|
|
|
UK2Node* TemplateConversionNode = nullptr;
|
|
|
|
if (TOptional<FFindSpecializedConversionNodeResults> ConversionResult = FindSpecializedConversionNode(OutputPin->PinType, *InputPin, true))
|
|
{
|
|
TemplateConversionNode = ConversionResult->TargetNode;
|
|
}
|
|
|
|
// Note: We return true if graph is modified, thus we should do all our desired checks, then chose to modify or not & return true / false appropriately
|
|
if (TemplateConversionNode != nullptr && TemplateConversionNode->IsA(UK2Node_DynamicCast::StaticClass()))
|
|
{
|
|
if (UK2Node_DynamicCast* DynamicCastNode = Cast<UK2Node_DynamicCast>(TemplateConversionNode))
|
|
{
|
|
if (UK2Node* InputPinNode = Cast<UK2Node>(InputPin->GetOwningNode()))
|
|
{
|
|
UEdGraphPin* InputExecPin = InputPinNode->GetExecPin();
|
|
|
|
if (InputExecPin && InputExecPin->LinkedTo.Num() == 1)
|
|
{
|
|
// By default Dynamic Cast Nodes generated by auto cast will be pure, convert to nonpure and handle exec connections
|
|
DynamicCastNode->SetPurity(false);
|
|
|
|
// Determine where to position the new node (assuming it isn't going to get beaded)
|
|
FVector2D AverageLocation = CalculateAveragePositionBetweenNodes(InputPin, OutputPin);
|
|
|
|
// Connect the cast node up to the output/input pins
|
|
UK2Node* ConversionNode = FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate<UK2Node>(InputPin->GetOwningNode()->GetGraph(), TemplateConversionNode, AverageLocation);
|
|
AutowireConversionNode(InputPin, OutputPin, ConversionNode);
|
|
|
|
// Reroute the exec connection on our target pin's node to have the dynamic cast. It must already be set.
|
|
UEdGraphPin* ToInputExecPin = InputExecPin->LinkedTo[0];
|
|
UEdGraphPin* CastExecPin = ConversionNode->GetExecPin();
|
|
UEdGraphPin* CastThenPin = ConversionNode->GetThenPin();
|
|
|
|
ToInputExecPin->BreakLinkTo(InputExecPin);
|
|
TryCreateConnection(ToInputExecPin, CastExecPin);
|
|
TryCreateConnection(CastThenPin, InputExecPin);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We couldn't handle the dynamic cast as desired, fail connections
|
|
return false;
|
|
}
|
|
|
|
return Super::CreateAutomaticConversionNodeAndConnections(PinA, PinB);
|
|
}
|
|
|
|
TOptional<UEdGraphSchema_K2::FFindSpecializedConversionNodeResults> UMVVMAsyncConversionFunctionGraphSchema::FindSpecializedConversionNode(const FEdGraphPinType& OutputPinType, const UEdGraphPin& InputPin, bool bCreateNode) const
|
|
{
|
|
TOptional<FFindSpecializedConversionNodeResults> Result = Super::FindSpecializedConversionNode(OutputPinType, InputPin, bCreateNode);
|
|
|
|
if (!Result.IsSet())
|
|
{
|
|
// In MVVM, allow autocast for object types where trivial
|
|
if (!OutputPinType.IsContainer())
|
|
{
|
|
FEdGraphPinType InputPinType = InputPin.PinType;
|
|
|
|
// Note: Cast nodes do not support a ForEach-style expansion, so we exclude container types here.
|
|
const UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(InputPin.GetOwningNode());
|
|
UClass* BlueprintClass = (Blueprint->GeneratedClass != nullptr) ? Blueprint->GeneratedClass : Blueprint->ParentClass;
|
|
|
|
UClass* InputClass = Cast<UClass>(InputPin.PinType.PinSubCategoryObject.Get());
|
|
|
|
if ((InputClass == nullptr) && (InputPin.PinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self))
|
|
{
|
|
InputClass = BlueprintClass;
|
|
}
|
|
|
|
const UClass* OutputClass = Cast<UClass>(OutputPinType.PinSubCategoryObject.Get());
|
|
|
|
if ((OutputClass == nullptr) && (OutputPinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self))
|
|
{
|
|
OutputClass = BlueprintClass;
|
|
}
|
|
|
|
bool bNeedsDynamicCast = false;
|
|
if ((OutputPinType.PinCategory == PC_Interface) && (InputPinType.PinCategory == PC_Object))
|
|
{
|
|
bNeedsDynamicCast = (InputClass && OutputClass) && (InputClass->ImplementsInterface(OutputClass) || OutputClass->IsChildOf(InputClass));
|
|
}
|
|
else if (OutputPinType.PinCategory == PC_Object)
|
|
{
|
|
if ((InputPinType.PinCategory == PC_Object))
|
|
{
|
|
bNeedsDynamicCast = (InputClass && OutputClass) && InputClass->IsChildOf(OutputClass);
|
|
}
|
|
}
|
|
|
|
if (bNeedsDynamicCast)
|
|
{
|
|
Result = { nullptr };
|
|
if (bCreateNode)
|
|
{
|
|
UK2Node_DynamicCast* DynCastNode = NewObject<UK2Node_DynamicCast>();
|
|
DynCastNode->TargetType = InputClass;
|
|
DynCastNode->SetPurity(true);
|
|
Result->TargetNode = DynCastNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
EGraphType UMVVMFakeTestUbergraphSchema::GetGraphType(const UEdGraph* TestEdGraph) const
|
|
{
|
|
return EGraphType::GT_Ubergraph;
|
|
}
|
|
|
|
UMVVMFakeTestUbergraph::UMVVMFakeTestUbergraph()
|
|
{
|
|
Schema = UMVVMFakeTestUbergraphSchema::StaticClass();
|
|
}
|
|
|
|
UMVVMFakeTestFunctiongraph::UMVVMFakeTestFunctiongraph()
|
|
{
|
|
Schema = UMVVMConversionFunctionGraphSchema::StaticClass();
|
|
}
|