Files
2025-05-18 13:04:45 +08:00

2520 lines
78 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "EdGraph/RigVMEdGraphSchema.h"
#include "EdGraph/RigVMEdGraph.h"
#include "EdGraph/RigVMEdGraphNode.h"
//#include "IControlRigEditorModule.h"
#include "RigVMCore/RigVMStruct.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "EdGraphNode_Comment.h"
#include "EdGraphSchema_K2_Actions.h"
#include "ScopedTransaction.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "GraphEditorActions.h"
#include "RigVMHost.h"
#include "RigVMBlueprint.h"
#include "Framework/Application/SlateApplication.h"
#include "Framework/Notifications/NotificationManager.h"
#include "RigVMStringUtils.h"
#include "Curves/CurveFloat.h"
#include "RigVMModel/Nodes/RigVMLibraryNode.h"
#include "RigVMModel/Nodes/RigVMCollapseNode.h"
#include "RigVMModel/Nodes/RigVMFunctionEntryNode.h"
#include "RigVMModel/Nodes/RigVMFunctionReturnNode.h"
#include "RigVMModel/RigVMVariableDescription.h"
#include "Kismet2/Kismet2NameValidators.h"
#include "Algo/Count.h"
#include "RigVMFunctions/RigVMDispatch_Array.h"
#include "RigVMFunctions/RigVMDispatch_Constant.h"
#include "RigVMFunctions/RigVMDispatch_MakeStruct.h"
#include "RigVMModel/Nodes/RigVMDispatchNode.h"
#if WITH_RIGVMLEGACYEDITOR
#include "BlueprintEditor.h"
#else
#include "Editor/RigVMNewEditor.h"
#endif
#include UE_INLINE_GENERATED_CPP_BY_NAME(RigVMEdGraphSchema)
#if WITH_EDITOR
#include "RigVMEditorModule.h"
#include "Misc/MessageDialog.h"
#include "Editor/Transactor.h"
#endif
#define LOCTEXT_NAMESPACE "CRigVMGraphSchema"
const FText FRigVMSearchTags::FiB_Name = LOCTEXT("Name", "Name");
const FText FRigVMSearchTags::FiB_ClassName = LOCTEXT("ClassName", "ClassName");
const FText FRigVMSearchTags::FiB_NodeGuid = LOCTEXT("NodeGuid", "NodeGuid");
const FText FRigVMSearchTags::FiB_PinCategory = LOCTEXT("PinCategory", "PinCategory");
const FText FRigVMSearchTags::FiB_PinSubCategory = LOCTEXT("SubCategory", "SubCategory");
const FText FRigVMSearchTags::FiB_PinBinding = LOCTEXT("Binding", "Binding");
const FText FRigVMSearchTags::FiB_ObjectClass = LOCTEXT("ObjectClass", "ObjectClass");
const FText FRigVMSearchTags::FiB_IsArray = LOCTEXT("IsArray", "IsArray");
const FText FRigVMSearchTags::FiB_Glyph = LOCTEXT("Glyph", "Glyph");
const FText FRigVMSearchTags::FiB_GlyphStyleSet = LOCTEXT("GlyphStyleSet", "GlyphStyleSet");
const FText FRigVMSearchTags::FiB_GlyphColor = LOCTEXT("GlyphColor", "GlyphColor");
FRigVMLocalVariableNameValidator::FRigVMLocalVariableNameValidator(const UBlueprint* Blueprint, const URigVMGraph* Graph, FName InExistingName)
: FStringSetNameValidator(InExistingName.ToString())
{
if (Blueprint)
{
TSet<FName> NamesTemp;
// We allow local variables with same name as blueprint variable
FBlueprintEditorUtils::GetFunctionNameList(Blueprint, NamesTemp);
FBlueprintEditorUtils::GetAllGraphNames(Blueprint, NamesTemp);
FBlueprintEditorUtils::GetSCSVariableNameList(Blueprint, NamesTemp);
FBlueprintEditorUtils::GetImplementingBlueprintsFunctionNameList(Blueprint, NamesTemp);
for (FName & Name : NamesTemp)
{
Names.Add(Name.ToString());
}
}
if (Graph)
{
for (const FRigVMGraphVariableDescription& LocalVariable : Graph->GetLocalVariables())
{
Names.Add(LocalVariable.Name.ToString());
}
for (const FRigVMGraphVariableDescription& InputArgument : Graph->GetInputArguments())
{
Names.Add(InputArgument.Name.ToString());
}
for (const FRigVMGraphVariableDescription& OutputArgument : Graph->GetOutputArguments())
{
Names.Add(OutputArgument.Name.ToString());
}
}
}
EValidatorResult FRigVMLocalVariableNameValidator::IsValid(const FString& Name, bool bOriginal)
{
const EValidatorResult Result = FStringSetNameValidator::IsValid(Name, bOriginal);
if (Result == EValidatorResult::Ok)
{
const URigVMSchema* RigSchema = Cast<URigVMSchema>(URigVMSchema::StaticClass()->GetDefaultObject(true));
if (RigSchema->GetSanitizedName(Name, false, true) == Name)
{
return Result;
}
return EValidatorResult::ContainsInvalidCharacters;
}
return Result;
}
EValidatorResult FRigVMLocalVariableNameValidator::IsValid(const FName& Name, bool bOriginal)
{
return IsValid(Name.ToString(), bOriginal);
}
FRigVMNameValidator::FRigVMNameValidator(const UBlueprint* Blueprint, const UStruct* ValidationScope, FName InExistingName)
: FStringSetNameValidator(InExistingName.ToString())
{
if (Blueprint)
{
TSet<FName> NamesTemp;
FBlueprintEditorUtils::GetClassVariableList(Blueprint, NamesTemp, true);
FBlueprintEditorUtils::GetFunctionNameList(Blueprint, NamesTemp);
FBlueprintEditorUtils::GetAllGraphNames(Blueprint, NamesTemp);
FBlueprintEditorUtils::GetSCSVariableNameList(Blueprint, NamesTemp);
FBlueprintEditorUtils::GetImplementingBlueprintsFunctionNameList(Blueprint, NamesTemp);
for (FName& Name : NamesTemp)
{
Names.Add(Name.ToString());
}
}
}
EValidatorResult FRigVMNameValidator::IsValid(const FString& Name, bool bOriginal)
{
const EValidatorResult Result = FStringSetNameValidator::IsValid(Name, bOriginal);
if (Result == EValidatorResult::Ok)
{
const URigVMSchema* RigSchema = Cast<URigVMSchema>(URigVMSchema::StaticClass()->GetDefaultObject(true));
if (RigSchema->GetSanitizedName(Name, false, true) == Name)
{
return Result;
}
return EValidatorResult::ContainsInvalidCharacters;
}
return Result;
}
EValidatorResult FRigVMNameValidator::IsValid(const FName& Name, bool bOriginal)
{
return IsValid(Name.ToString(), bOriginal);
}
FEdGraphPinType FRigVMEdGraphSchemaAction_LocalVar::GetPinType() const
{
if (const URigVMEdGraph* Graph = Cast<URigVMEdGraph>(GetVariableScope()))
{
for (FRigVMGraphVariableDescription Variable : Graph->GetModel()->GetLocalVariables())
{
if (Variable.Name == GetVariableName())
{
return Variable.ToPinType();
}
}
for (FRigVMGraphVariableDescription Variable : Graph->GetModel()->GetInputArguments())
{
if (Variable.Name == GetVariableName())
{
return Variable.ToPinType();
}
}
}
return FEdGraphPinType();
}
void FRigVMEdGraphSchemaAction_LocalVar::ChangeVariableType(const FEdGraphPinType& NewPinType)
{
if (const URigVMEdGraph* Graph = Cast<URigVMEdGraph>(GetVariableScope()))
{
FString NewCPPType;
UObject* NewCPPTypeObject = nullptr;
RigVMTypeUtils::CPPTypeFromPinType(NewPinType, NewCPPType, &NewCPPTypeObject);
Graph->GetController()->SetLocalVariableType(GetVariableName(), NewCPPType, NewCPPTypeObject, true, true);
}
}
void FRigVMEdGraphSchemaAction_LocalVar::RenameVariable(const FName& NewName)
{
const FName OldName = GetVariableName();
if (OldName == NewName)
{
return;
}
if (const URigVMEdGraph* Graph = Cast<URigVMEdGraph>(GetVariableScope()))
{
if (Graph->GetController()->RenameLocalVariable(OldName, NewName, true, true))
{
SetVariableInfo(NewName, GetVariableScope(), GetPinType().PinCategory == TEXT("bool"));
}
}
}
bool FRigVMEdGraphSchemaAction_LocalVar::IsValidName(const FName& NewName, FText& OutErrorMessage) const
{
if (const URigVMEdGraph* EdGraphGraph = Cast<URigVMEdGraph>(GetVariableScope()))
{
FRigVMLocalVariableNameValidator NameValidator(EdGraphGraph->GetBlueprint(), EdGraphGraph->GetModel(), GetVariableName());
const EValidatorResult Result = NameValidator.IsValid(NewName.ToString(), false);
if (Result != EValidatorResult::Ok && Result != EValidatorResult::ExistingName)
{
OutErrorMessage = FText::FromString(TEXT("Name with invalid format"));
return false;
}
}
return true;
}
void FRigVMEdGraphSchemaAction_LocalVar::DeleteVariable()
{
if (const URigVMEdGraph* Graph = Cast<URigVMEdGraph>(GetVariableScope()))
{
#if WITH_EDITOR
if (GEditor)
{
GEditor->CancelTransaction(0);
}
#endif
Graph->GetController()->RemoveLocalVariable(GetVariableName(), true, true);
}
}
bool FRigVMEdGraphSchemaAction_LocalVar::IsVariableUsed()
{
if (const URigVMEdGraph* EdGraph = Cast<URigVMEdGraph>(GetVariableScope()))
{
const FString VarNameStr = GetVariableName().ToString();
for (URigVMNode* Node : EdGraph->GetModel()->GetNodes())
{
if (const URigVMVariableNode* VarNode = Cast<URigVMVariableNode>(Node))
{
if (VarNode->FindPin(TEXT("Variable"))->GetDefaultValue() == VarNameStr)
{
return true;
}
}
}
}
return false;
}
FRigVMEdGraphSchemaAction_PromoteToVariable::FRigVMEdGraphSchemaAction_PromoteToVariable(UEdGraphPin* InEdGraphPin, bool InLocalVariable)
: FEdGraphSchemaAction( FText(),
InLocalVariable ? LOCTEXT("PromoteToLocalVariable", "Promote to local variable") : LOCTEXT("PromoteToVariable", "Promote to variable"),
InLocalVariable ? LOCTEXT("PromoteToLocalVariable", "Promote to local variable") : LOCTEXT("PromoteToVariable", "Promote to variable"),
1)
, EdGraphPin(InEdGraphPin)
, bLocalVariable(InLocalVariable)
{
}
UEdGraphNode* FRigVMEdGraphSchemaAction_PromoteToVariable::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin,
const FVector2f& Location, bool bSelectNewNode)
{
URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(ParentGraph);
if(RigGraph == nullptr)
{
return nullptr;
}
URigVMBlueprint* Blueprint = RigGraph->GetBlueprint();
URigVMGraph* Model = RigGraph->GetModel();
URigVMController* Controller = RigGraph->GetController();
if((Blueprint == nullptr) ||
(Model == nullptr) ||
(Controller == nullptr))
{
return nullptr;
}
URigVMPin* ModelPin = Model->FindPin(FromPin->GetName());
FName VariableName(NAME_None);
const FScopedTransaction Transaction(
bLocalVariable ?
LOCTEXT("GraphEd_PromoteToLocalVariable", "Promote Pin To Local Variable") :
LOCTEXT("GraphEd_PromoteToVariable", "Promote Pin To Variable"));
if(bLocalVariable)
{
#if WITH_EDITOR
if (GEditor)
{
GEditor->CancelTransaction(0);
}
#endif
const FRigVMGraphVariableDescription VariableDescription = Controller->AddLocalVariable(
*ModelPin->GetPinPath(),
ModelPin->GetCPPType(),
ModelPin->GetCPPTypeObject(),
ModelPin->GetDefaultValue(),
true,
true
);
VariableName = VariableDescription.Name;
}
else
{
Blueprint->Modify();
FString DefaultValue = ModelPin->GetDefaultValue();
if(!DefaultValue.IsEmpty())
{
if(UScriptStruct* ScriptStruct = Cast<UScriptStruct>(ModelPin->GetCPPTypeObject()))
{
if(ScriptStruct == TBaseStructure<FVector2D>::Get())
{
FVector2D Value = FVector2D::ZeroVector;
ScriptStruct->ImportText(*DefaultValue, &Value, nullptr, PPF_None, nullptr, FString());
DefaultValue = Value.ToString();
}
if(ScriptStruct == TBaseStructure<FVector>::Get())
{
FVector Value = FVector::ZeroVector;
ScriptStruct->ImportText(*DefaultValue, &Value, nullptr, PPF_None, nullptr, FString());
DefaultValue = Value.ToString();
}
if(ScriptStruct == TBaseStructure<FQuat>::Get())
{
FQuat Value = FQuat::Identity;
ScriptStruct->ImportText(*DefaultValue, &Value, nullptr, PPF_None, nullptr, FString());
DefaultValue = Value.ToString();
}
if(ScriptStruct == TBaseStructure<FRotator>::Get())
{
FRotator Value = FRotator::ZeroRotator;
ScriptStruct->ImportText(*DefaultValue, &Value, nullptr, PPF_None, nullptr, FString());
DefaultValue = Value.ToString();
}
if(ScriptStruct == TBaseStructure<FTransform>::Get())
{
FTransform Value = FTransform::Identity;
ScriptStruct->ImportText(*DefaultValue, &Value, nullptr, PPF_None, nullptr, FString());
DefaultValue = Value.ToString();
}
}
}
FRigVMExternalVariable ExternalVariable;
ExternalVariable.Name = FromPin->GetFName();
ExternalVariable.bIsArray = ModelPin->IsArray();
ExternalVariable.TypeName = ModelPin->IsArray() ? *ModelPin->GetArrayElementCppType() : *ModelPin->GetCPPType();
ExternalVariable.TypeObject = ModelPin->GetCPPTypeObject();
VariableName = Blueprint->AddHostMemberVariableFromExternal(
ExternalVariable,
DefaultValue
);
}
if(!VariableName.IsNone())
{
const URigVMNode* ModelNode = Controller->AddVariableNode(
VariableName,
ModelPin->GetCPPType(),
ModelPin->GetCPPTypeObject(),
FromPin->Direction == EGPD_Input,
ModelPin->GetDefaultValue(),
FDeprecateSlateVector2D(Location),
FString(),
true,
true
);
if(ModelNode)
{
if(FromPin->Direction == EGPD_Input)
{
Controller->AddLink(ModelNode->FindPin(TEXT("Value")), ModelPin, true);
}
else
{
Controller->AddLink(ModelPin, ModelNode->FindPin(TEXT("Value")), true);
}
return RigGraph->FindNodeForModelNodeName(ModelNode->GetFName());
}
}
return nullptr;
}
FRigVMEdGraphSchemaAction_PromoteToExposedPin::FRigVMEdGraphSchemaAction_PromoteToExposedPin(UEdGraphPin* InEdGraphPin)
: FEdGraphSchemaAction( FText(),
LOCTEXT("PromoteToExposedPin", "Promote to exposed pin"),
LOCTEXT("PromoteToExposedPin", "Promote to exposed pin"),
1)
, EdGraphPin(InEdGraphPin)
{
}
UEdGraphNode* FRigVMEdGraphSchemaAction_PromoteToExposedPin::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode)
{
URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(ParentGraph);
if(RigGraph == nullptr)
{
return nullptr;
}
const URigVMGraph* Model = RigGraph->GetModel();
URigVMController* Controller = RigGraph->GetController();
if(Model == nullptr || Controller == nullptr)
{
return nullptr;
}
URigVMPin* ModelPin = Model->FindPin(FromPin->GetName());
if (ModelPin == nullptr)
{
return nullptr;
}
const FScopedTransaction Transaction(
LOCTEXT("GraphEd_PromoteToExposedPin", "Promote To Exposed Pin"));
#if WITH_EDITOR
if (GEditor)
{
GEditor->CancelTransaction(0);
}
#endif
Controller->OpenUndoBracket(TEXT("Promote to Exposed Pin"));
const UObject* CPPTypeObject = ModelPin->GetCPPTypeObject();
const FString PinName = Controller->AddExposedPin(
*ModelPin->GetName(),
ModelPin->GetDirection(),
ModelPin->GetCPPType(),
CPPTypeObject ? (FName)*CPPTypeObject->GetPathName() : NAME_None,
ModelPin->GetDefaultValue(),
true,
true
).ToString();
UEdGraphNode* Result = nullptr;
if(!PinName.IsEmpty())
{
if (ModelPin->GetDirection() == ERigVMPinDirection::Input)
{
Controller->AddLink(Model->GetEntryNode()->FindPin(PinName), ModelPin, true);
Result = RigGraph->FindNodeForModelNodeName(Model->GetEntryNode()->GetFName());
}
else if(ModelPin->GetDirection() == ERigVMPinDirection::Output)
{
Controller->AddLink(ModelPin, Model->GetReturnNode()->FindPin(PinName), true);
Result = RigGraph->FindNodeForModelNodeName(Model->GetReturnNode()->GetFName());
}
else if (ModelPin->GetDirection() == ERigVMPinDirection::IO)
{
const URigVMFunctionEntryNode* EntryNode = Model->GetEntryNode();
Controller->AddLink(EntryNode->FindPin(PinName), ModelPin, true);
Controller->AddLink(ModelPin, Model->GetReturnNode()->FindPin(PinName), true);
Result = RigGraph->FindNodeForModelNodeName(EntryNode->GetFName());
}
}
Controller->CloseUndoBracket();
return Result;
}
FRigVMEdGraphSchemaAction_Event::FRigVMEdGraphSchemaAction_Event(const FName& InEventName, const FString& InNodePath, const FText& InNodeCategory)
: FEdGraphSchemaAction( InNodeCategory,
FText::FromName(InEventName),
FText(),
1)
, NodePath(InNodePath)
{
}
FReply FRigVMEdGraphSchemaAction_Event::OnDoubleClick(UBlueprint* InBlueprint)
{
if (URigVMBlueprint* Blueprint = Cast<URigVMBlueprint>(InBlueprint))
{
if(const URigVMNode* ModelNode = Blueprint->GetRigVMClient()->FindNode(NodePath))
{
Blueprint->OnRequestJumpToHyperlink().Execute(ModelNode);
return FReply::Handled();
}
}
return FReply::Unhandled();;
}
FSlateBrush const* FRigVMEdGraphSchemaAction_Event::GetPaletteIcon() const
{
static FSlateIcon EventIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.Event_16x");
return EventIcon.GetIcon();
}
FReply FRigVMFunctionDragDropAction::DroppedOnPanel(const TSharedRef< class SWidget >& Panel, const FVector2f& ScreenPosition, const FVector2f& GraphPosition, UEdGraph& Graph)
{
// For local variables
if (SourceAction->GetTypeId() == FRigVMEdGraphSchemaAction_LocalVar::StaticGetTypeId())
{
if (URigVMEdGraph* TargetRigGraph = Cast<URigVMEdGraph>(&Graph))
{
if (TargetRigGraph == SourceRigGraph)
{
FRigVMEdGraphSchemaAction_LocalVar* VarAction = (FRigVMEdGraphSchemaAction_LocalVar*) SourceAction.Get();
for (FRigVMGraphVariableDescription LocalVariable : TargetRigGraph->GetModel()->GetLocalVariables())
{
if (LocalVariable.Name == VarAction->GetVariableName())
{
URigVMController* Controller = TargetRigGraph->GetController();
FMenuBuilder MenuBuilder(true, nullptr);
const FText VariableNameText = FText::FromName( LocalVariable.Name );
MenuBuilder.BeginSection("BPVariableDroppedOn", VariableNameText );
MenuBuilder.AddMenuEntry(
FText::Format( LOCTEXT("CreateGetVariable", "Get {0}"), VariableNameText ),
FText::Format( LOCTEXT("CreateVariableGetterToolTip", "Create Getter for variable '{0}'\n(Ctrl-drag to automatically create a getter)"), VariableNameText ),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateLambda([Controller, LocalVariable, GraphPosition]()
{
Controller->AddVariableNode(LocalVariable.Name, LocalVariable.CPPType, LocalVariable.CPPTypeObject, true, LocalVariable.DefaultValue, FDeprecateSlateVector2D(GraphPosition), FString(), true, true);
}),
FCanExecuteAction()));
MenuBuilder.AddMenuEntry(
FText::Format( LOCTEXT("CreateSetVariable", "Set {0}"), VariableNameText ),
FText::Format( LOCTEXT("CreateVariableSetterToolTip", "Create Setter for variable '{0}'\n(Alt-drag to automatically create a setter)"), VariableNameText ),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateLambda([Controller, LocalVariable, GraphPosition]()
{
Controller->AddVariableNode(LocalVariable.Name, LocalVariable.CPPType, LocalVariable.CPPTypeObject, false, LocalVariable.DefaultValue, FDeprecateSlateVector2D(GraphPosition), FString(), true, true);
}),
FCanExecuteAction()));
TSharedRef< SWidget > PanelWidget = Panel;
// Show dialog to choose getter vs setter
FSlateApplication::Get().PushMenu(
PanelWidget,
FWidgetPath(),
MenuBuilder.MakeWidget(),
ScreenPosition,
FPopupTransitionEffect( FPopupTransitionEffect::ContextMenu)
);
MenuBuilder.EndSection();
}
}
}
}
}
// For functions
else if (URigVMEdGraph* TargetRigGraph = Cast<URigVMEdGraph>(&Graph))
{
if (URigVMBlueprint* TargetRigBlueprint = Cast<URigVMBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(TargetRigGraph)))
{
if (URigVMGraph* FunctionDefinitionGraph = SourceRigBlueprint->GetModel(SourceRigGraph))
{
if (URigVMLibraryNode* FunctionDefinitionNode = Cast<URigVMLibraryNode>(FunctionDefinitionGraph->GetOuter()))
{
if(URigVMController* TargetController = TargetRigBlueprint->GetController(TargetRigGraph))
{
if(URigVMFunctionLibrary* FunctionLibrary = Cast<URigVMFunctionLibrary>(FunctionDefinitionNode->GetOuter()))
{
if(URigVMBlueprint* FunctionRigBlueprint = Cast<URigVMBlueprint>(FunctionLibrary->GetOuter()))
{
#if WITH_EDITOR
if(FunctionRigBlueprint != TargetRigBlueprint)
{
if(!FunctionRigBlueprint->IsFunctionPublic(FunctionDefinitionNode->GetFName()))
{
FRigVMGraphFunctionIdentifier FunctionIdentifier = FunctionDefinitionNode->GetFunctionIdentifier();
TargetRigBlueprint->BroadcastRequestLocalizeFunctionDialog(FunctionIdentifier);
FunctionDefinitionNode = TargetRigBlueprint->GetLocalFunctionLibrary()->FindPreviouslyLocalizedFunction(FunctionIdentifier);
}
}
#endif
TargetController->AddFunctionReferenceNode(FunctionDefinitionNode, FDeprecateSlateVector2D(GraphPosition), FString(), true, true);
}
}
}
}
}
}
}
return FReply::Unhandled();
}
FReply FRigVMFunctionDragDropAction::DroppedOnPin(const FVector2f& ScreenPosition, const FVector2f& GraphPosition)
{
return FReply::Unhandled();
}
FReply FRigVMFunctionDragDropAction::DroppedOnAction(TSharedRef<FEdGraphSchemaAction> Action)
{
return FReply::Unhandled();
}
FReply FRigVMFunctionDragDropAction::DroppedOnCategory(FText Category)
{
// todo
/*
if (SourceAction.IsValid())
{
SourceAction->MovePersistentItemToCategory(Category);
}
*/
return FReply::Unhandled();
}
void FRigVMFunctionDragDropAction::HoverTargetChanged()
{
// todo - see FMyBlueprintItemDragDropAction
FGraphSchemaActionDragDropAction::HoverTargetChanged();
// check for category + graph, everything else we won't allow for now.
bDropTargetValid = true;
}
FRigVMFunctionDragDropAction::FRigVMFunctionDragDropAction()
: FGraphSchemaActionDragDropAction()
, SourceRigBlueprint(nullptr)
, SourceRigGraph(nullptr)
, bControlDrag(false)
, bAltDrag(false)
{
}
TSharedRef<FRigVMFunctionDragDropAction> FRigVMFunctionDragDropAction::New(TSharedPtr<FEdGraphSchemaAction> InAction, URigVMBlueprint* InRigBlueprint, URigVMEdGraph* InRigGraph)
{
TSharedRef<FRigVMFunctionDragDropAction> Action = MakeShareable(new FRigVMFunctionDragDropAction);
Action->SourceAction = InAction;
Action->SourceRigBlueprint = InRigBlueprint;
Action->SourceRigGraph = InRigGraph;
Action->Construct();
return Action;
}
URigVMEdGraphSchema::URigVMEdGraphSchema()
{
}
void URigVMEdGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const
{
}
void URigVMEdGraphSchema::GetContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const
{
/*
// this seems to be taken care of by ControlRigGraphNode
#if WITH_EDITOR
return IControlRigEditorModule::Get().GetContextMenuActions(this, Menu, Context);
#else
check(0);
#endif
*/
}
bool URigVMEdGraphSchema::TryCreateConnection(UEdGraphPin* PinA, UEdGraphPin* PinB) const
{
#if WITH_EDITOR
if (GEditor)
{
GEditor->CancelTransaction(0);
}
#endif
if (PinA == PinB)
{
return false;
}
if (PinA->GetOwningNode() == PinB->GetOwningNode())
{
return false;
}
IRigVMClientHost* Host = PinA->GetOwningNode()->GetImplementingOuter<IRigVMClientHost>();
if (Host != nullptr)
{
if (URigVMController* Controller = Host->GetRigVMClient()->GetOrCreateController(PinA->GetOwningNode()->GetGraph()))
{
ERigVMPinDirection UserLinkDirection = ERigVMPinDirection::Output;
if (PinA->Direction == EGPD_Input)
{
Swap(PinA, PinB);
UserLinkDirection = ERigVMPinDirection::Input;
}
const bool bPinAIsWildCard = PinA->PinType.PinSubCategoryObject == RigVMTypeUtils::GetWildCardCPPTypeObject();
const bool bPinBIsWildCard = PinB->PinType.PinSubCategoryObject == RigVMTypeUtils::GetWildCardCPPTypeObject();
if(bPinAIsWildCard != bPinBIsWildCard)
{
// switch the user link direction if only one of the pins is a wildcard
if(UserLinkDirection == ERigVMPinDirection::Input && bPinBIsWildCard)
{
UserLinkDirection = ERigVMPinDirection::Output;
}
else if(UserLinkDirection == ERigVMPinDirection::Output && bPinAIsWildCard)
{
UserLinkDirection = ERigVMPinDirection::Input;
}
}
#if WITH_EDITOR
static const FText LoopMessage = LOCTEXT("LinkingLoopNotRecommended", "Linking a function return within a loop is not recommended.\nAre you sure?");
// check if we are trying to connect a loop iteration pin to a return
if(const URigVMGraph* Graph = Controller->GetGraph())
{
if(const URigVMPin* TargetPin = Graph->FindPin(PinB->GetName()))
{
if(TargetPin->IsExecuteContext() && TargetPin->GetNode()->IsA<URigVMFunctionReturnNode>())
{
bool bIsInLoopIteration = false;
if(const URigVMPin* SourcePin = Graph->FindPin(PinA->GetName()))
{
while(SourcePin)
{
if(!SourcePin->IsExecuteContext())
{
break;
}
const URigVMPin* CurrentSourcePin = SourcePin;
SourcePin = nullptr;
if(const URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(CurrentSourcePin->GetNode()))
{
TSharedPtr<FStructOnScope> UnitScope = UnitNode->ConstructStructInstance();
if(UnitScope.IsValid())
{
const FRigVMStruct* Unit = (FRigVMStruct*)UnitScope->GetStructMemory();
if(Unit->IsForLoop())
{
if(CurrentSourcePin->GetFName() != FRigVMStruct::ForLoopCompletedPinName)
{
bIsInLoopIteration = true;
break;
}
}
}
}
for(const URigVMPin* PinOnSourceNode : CurrentSourcePin->GetNode()->GetPins())
{
if(!PinOnSourceNode->IsExecuteContext())
{
continue;
}
if(PinOnSourceNode->GetDirection() != ERigVMPinDirection::Input &&
PinOnSourceNode->GetDirection() != ERigVMPinDirection::IO)
{
continue;
}
TArray<URigVMPin*> NextSourcePins = PinOnSourceNode->GetLinkedSourcePins();
if(NextSourcePins.Num() > 0)
{
SourcePin = NextSourcePins[0];
break;
}
}
}
}
if(bIsInLoopIteration)
{
const EAppReturnType::Type Answer = FMessageDialog::Open( EAppMsgType::YesNo, LoopMessage );
if(Answer == EAppReturnType::No)
{
return false;
}
}
}
}
}
#endif
const bool bCreateCastNode = !FSlateApplication::Get().GetModifierKeys().IsAltDown();
return Controller->AddLink(PinA->GetName(), PinB->GetName(), true, true, UserLinkDirection, bCreateCastNode);
}
}
return false;
}
static bool HasParentConnection_Recursive(const UEdGraphPin* InPin)
{
if(InPin->ParentPin)
{
return InPin->ParentPin->LinkedTo.Num() > 0 || HasParentConnection_Recursive(InPin->ParentPin);
}
return false;
}
static bool HasChildConnection_Recursive(const UEdGraphPin* InPin)
{
for(const UEdGraphPin* SubPin : InPin->SubPins)
{
if(SubPin->LinkedTo.Num() > 0 || HasChildConnection_Recursive(SubPin))
{
return true;
}
}
return false;
}
const FPinConnectionResponse URigVMEdGraphSchema::CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const
{
if (A == nullptr || B == nullptr)
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("One of the Pins is NULL"));
}
const URigVMEdGraphNode* RigNodeA = Cast<URigVMEdGraphNode>(A->GetOwningNode());
const URigVMEdGraphNode* RigNodeB = Cast<URigVMEdGraphNode>(B->GetOwningNode());
if (RigNodeA && RigNodeB && RigNodeA != RigNodeB)
{
URigVMPin* PinA = RigNodeA->GetModelPinFromPinPath(A->GetName());
if (PinA)
{
PinA = PinA->GetPinForLink();
RigNodeA->GetModel()->PrepareCycleChecking(PinA, A->Direction == EGPD_Input);
}
URigVMPin* PinB = RigNodeB->GetModelPinFromPinPath(B->GetName());
if (PinB)
{
PinB = PinB->GetPinForLink();
}
ERigVMPinDirection UserLinkDirection = ERigVMPinDirection::Output;
if (A->Direction == EGPD_Input)
{
Swap(PinA, PinB);
UserLinkDirection = ERigVMPinDirection::Input;
}
if (!PinA)
{
return FPinConnectionResponse(ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW, FString::Printf(TEXT("Pin %s not found"), *A->GetName()));
}
if (!PinB)
{
return FPinConnectionResponse(ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW, FString::Printf(TEXT("Pin %s not found"), *B->GetName()));
}
if(PinA->IsWildCard() != PinB->IsWildCard())
{
// switch the user link direction if only one of the pins is a wildcard
if(UserLinkDirection == ERigVMPinDirection::Input && PinB->IsWildCard())
{
UserLinkDirection = ERigVMPinDirection::Output;
}
else if(UserLinkDirection == ERigVMPinDirection::Output && PinA->IsWildCard())
{
UserLinkDirection = ERigVMPinDirection::Input;
}
}
const FRigVMByteCode* ByteCode = RigNodeA->GetController()->GetCurrentByteCode();
const bool bEnableTypeCasting = !FSlateApplication::Get().GetModifierKeys().IsAltDown();
FString FailureReason;
const bool bResult = RigNodeA->GetModel()->CanLink(PinA, PinB, &FailureReason, ByteCode, UserLinkDirection, bEnableTypeCasting);
if (!bResult)
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, FText::FromString(FailureReason));
}
return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, LOCTEXT("ConnectResponse_Allowed", "Connect"));
}
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("ConnectResponse_Disallowed_Unexpected", "Unexpected error"));
}
FLinearColor URigVMEdGraphSchema::GetPinTypeColor(const FEdGraphPinType& PinType) const
{
const FName& TypeName = PinType.PinCategory;
if (TypeName == UEdGraphSchema_K2::PC_Struct)
{
if (const UStruct* Struct = Cast<UStruct>(PinType.PinSubCategoryObject))
{
if (Struct->IsChildOf(FRigVMExecutePin::StaticStruct()))
{
return FLinearColor::White;
}
if (Struct->IsChildOf(RigVMTypeUtils::GetWildCardCPPTypeObject()))
{
return FLinearColor(FVector3f::OneVector * 0.25f);
}
}
}
return GetDefault<UEdGraphSchema_K2>()->GetPinTypeColor(PinType);
}
void URigVMEdGraphSchema::InsertAdditionalActions(TArray<UBlueprint*> InBlueprints, TArray<UEdGraph*> EdGraphs,
TArray<UEdGraphPin*> EdGraphPins, FGraphActionListBuilderBase& OutAllActions) const
{
Super::InsertAdditionalActions(InBlueprints, EdGraphs, EdGraphPins, OutAllActions);
if(EdGraphPins.Num() > 0)
{
if(const URigVMEdGraphNode* RigNode = Cast<URigVMEdGraphNode>(EdGraphPins[0]->GetOwningNode()))
{
if(const URigVMPin* ModelPin = RigNode->GetModelPinFromPinPath(EdGraphPins[0]->GetName()))
{
const bool bIsRootGraph = ModelPin->GetGraph()->IsRootGraph();
if(!ModelPin->IsExecuteContext() && !ModelPin->IsWildCard())
{
if(!ModelPin->GetNode()->IsA<URigVMVariableNode>())
{
OutAllActions.AddAction(TSharedPtr<FRigVMEdGraphSchemaAction_PromoteToVariable>(
new FRigVMEdGraphSchemaAction_PromoteToVariable(EdGraphPins[0], false)
));
if(!bIsRootGraph && !ModelPin->IsWildCard())
{
OutAllActions.AddAction(TSharedPtr<FRigVMEdGraphSchemaAction_PromoteToVariable>(
new FRigVMEdGraphSchemaAction_PromoteToVariable(EdGraphPins[0], true)
));
}
}
}
if (!bIsRootGraph)
{
if (!ModelPin->GetGraph()->GetRootGraph()->IsA<URigVMFunctionLibrary>() || !ModelPin->IsWildCard())
{
OutAllActions.AddAction(TSharedPtr<FRigVMEdGraphSchemaAction_PromoteToExposedPin>(
new FRigVMEdGraphSchemaAction_PromoteToExposedPin(EdGraphPins[0])
));
}
}
}
}
}
}
TSharedPtr<INameValidatorInterface> URigVMEdGraphSchema::GetNameValidator(const UBlueprint* BlueprintObj, const FName& OriginalName, const UStruct* ValidationScope, const FName& ActionTypeId) const
{
if (ActionTypeId == FRigVMEdGraphSchemaAction_LocalVar::StaticGetTypeId())
{
// this cast will always fail, URigVMEdGraph is not a UStruct
/*
if (const URigVMEdGraph* EdGraph = Cast<URigVMEdGraph>(ValidationScope))
{
if (const URigVMGraph* Graph = EdGraph->GetModel())
{
return MakeShareable(new FRigVMLocalVariableNameValidator(BlueprintObj, Graph, OriginalName));
}
}
*/
}
return MakeShareable(new FRigVMNameValidator(BlueprintObj, ValidationScope, OriginalName));
}
bool URigVMEdGraphSchema::CanPromotePinToVariable(const UEdGraphPin& Pin, bool bCond) const
{
return true;
}
bool URigVMEdGraphSchema::SupportsPinType(const UScriptStruct* ScriptStruct) const
{
if(!ScriptStruct)
{
return false;
}
if(ScriptStruct->IsChildOf(FRigVMStruct::StaticStruct()))
{
return false;
}
if(ScriptStruct->IsChildOf(FRigVMDispatchFactory::StaticStruct()))
{
return false;
}
// todo: Validate ExecuteContext structs to match URigVMBlueprint::GetExecuteContextStruct()
for (TFieldIterator<FProperty> It(ScriptStruct); It; ++It)
{
FProperty* Property = *It;
if (const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
{
Property = ArrayProperty->Inner;
}
FString CPPType = Property->GetCPPType();
if (CPPType == TEXT("bool") ||
CPPType == TEXT("float") ||
CPPType == TEXT("double") ||
CPPType == TEXT("int32") ||
CPPType == TEXT("FString") ||
CPPType == TEXT("FName") ||
CPPType == TEXT("uint16"))
{
continue;
}
if (const FStructProperty* StructProperty = CastField<FStructProperty>(Property))
{
if (SupportsPinType(StructProperty->Struct))
{
continue;
}
}
else if (Property->IsA<FEnumProperty>())
{
continue;
}
else if (const FByteProperty* ByteProperty = CastField<FByteProperty>(Property))
{
if (ByteProperty->Enum)
{
continue;
}
}
else if (CastField<FObjectProperty>(Property) && RigVMCore::SupportsUObjects())
{
continue;
}
else if (CastField<FInterfaceProperty>(Property) && RigVMCore::SupportsUInterfaces())
{
continue;
}
return false;
}
return true;
}
bool URigVMEdGraphSchema::SupportsPinType(TWeakPtr<const FEdGraphSchemaAction> SchemaAction, const FEdGraphPinType& PinType) const
{
if (PinType.IsContainer())
{
return false;
}
const FName TypeName = PinType.PinCategory;
if (TypeName == UEdGraphSchema_K2::PC_Boolean ||
TypeName == UEdGraphSchema_K2::PC_Int ||
TypeName == UEdGraphSchema_K2::PC_Real ||
TypeName == UEdGraphSchema_K2::PC_Name ||
TypeName == UEdGraphSchema_K2::PC_String ||
TypeName == UEdGraphSchema_K2::PC_Enum)
{
return true;
}
if(RigVMCore::SupportsUObjects())
{
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Object ||
PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject ||
PinType.PinCategory == UEdGraphSchema_K2::AllObjectTypes)
{
if (PinType.PinSubCategoryObject.IsValid())
{
return PinType.PinSubCategoryObject->IsA<UClass>();
}
}
}
if (RigVMCore::SupportsUInterfaces())
{
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Interface)
{
if (PinType.PinSubCategoryObject.IsValid())
{
return PinType.PinSubCategoryObject->IsA<UInterface>();
}
}
}
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Struct)
{
if (const UScriptStruct* ScriptStruct = Cast<UScriptStruct>(PinType.PinSubCategoryObject))
{
if(SchemaAction.IsValid() && SchemaAction.Pin()->IsAVariable())
{
if(ScriptStruct->IsChildOf(FRigVMExecutePin::StaticStruct()))
{
return false;
}
}
return SupportsPinType(ScriptStruct);
}
else if (PinType.PinSubCategoryObject == UUserDefinedStruct::StaticClass())
{
// if a user defined struct hasn't been loaded yet,
// its PinSubCategoryObject equals UUserDefinedStruct::StaticClass()
// and since it is not practical to load every user defined struct to check if they only contain supported types,
// we always return true so that they at least show up in the drop down menu
// if they contain members of unsupported type, the spawned node will generate error
return true;
}
}
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Byte)
{
if (PinType.PinSubCategoryObject.IsValid())
{
return PinType.PinSubCategoryObject->IsA<UEnum>();
}
}
return false;
}
bool URigVMEdGraphSchema::SupportsPinTypeContainer(TWeakPtr<const FEdGraphSchemaAction> SchemaAction,
const FEdGraphPinType& PinType, const EPinContainerType& ContainerType) const
{
// Do not allow containers for execute context type
if(const UScriptStruct* ExecuteContextScriptStruct = Cast<UScriptStruct>(PinType.PinSubCategoryObject))
{
if (ExecuteContextScriptStruct->IsChildOf(FRigVMExecutePin::StaticStruct()))
{
return ContainerType == EPinContainerType::None;
}
}
return ContainerType == EPinContainerType::None || ContainerType == EPinContainerType::Array;
}
void URigVMEdGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const
{
//const FScopedTransaction Transaction( LOCTEXT("GraphEd_BreakPinLinks", "Break Pin Links") );
// cache this here, as BreakPinLinks can trigger a node reconstruction invalidating the TargetPin referenceS
if (const URigVMEdGraphNode* Node = Cast< URigVMEdGraphNode>(TargetPin.GetOwningNode()))
{
Node->GetController()->BreakAllLinks(TargetPin.GetName(), TargetPin.Direction == EGPD_Input, true, true);
}
}
void URigVMEdGraphSchema::BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin) const
{
//const FScopedTransaction Transaction(LOCTEXT("GraphEd_BreakSinglePinLink", "Break Pin Link") );
if (const URigVMEdGraphNode* Node = Cast< URigVMEdGraphNode>(TargetPin->GetOwningNode()))
{
if (SourcePin->Direction == EGPD_Input)
{
UEdGraphPin* Temp = TargetPin;
TargetPin = SourcePin;
SourcePin = Temp;
}
Node->GetController()->BreakLink(SourcePin->GetName(), TargetPin->GetName(), true, true);
}
}
bool URigVMEdGraphSchema::CanGraphBeDropped(TSharedPtr<FEdGraphSchemaAction> InAction) const
{
if (!InAction.IsValid())
{
return false;
}
if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId())
{
const FEdGraphSchemaAction_K2Graph* FuncAction = (FEdGraphSchemaAction_K2Graph*)InAction.Get();
if (Cast<URigVMEdGraph>(FuncAction->EdGraph))
{
return true;
}
}
else if (InAction->GetTypeId() == FRigVMEdGraphSchemaAction_LocalVar::StaticGetTypeId())
{
const FRigVMEdGraphSchemaAction_LocalVar* VarAction = (FRigVMEdGraphSchemaAction_LocalVar*)InAction.Get();
if (Cast<URigVMEdGraph>((UEdGraph*)VarAction->GetVariableScope()))
{
return true;
}
}
return false;
}
FString URigVMEdGraphSchema::GetFindReferenceSearchTerm(const FEdGraphSchemaAction* InGraphAction) const
{
if(InGraphAction)
{
if(InGraphAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId())
{
const FEdGraphSchemaAction_K2Var* VarAction = (const FEdGraphSchemaAction_K2Var*)InGraphAction;
return VarAction->GetVariableName().ToString();
}
else if (InGraphAction->GetTypeId() == FRigVMEdGraphSchemaAction_LocalVar::StaticGetTypeId())
{
const FRigVMEdGraphSchemaAction_LocalVar* VarAction = (const FRigVMEdGraphSchemaAction_LocalVar*)InGraphAction;
return VarAction->GetVariableName().ToString();
}
}
return Super::GetFindReferenceSearchTerm(InGraphAction);
}
FReply URigVMEdGraphSchema::BeginGraphDragAction(TSharedPtr<FEdGraphSchemaAction> InAction, const FPointerEvent& MouseEvent) const
{
if (!InAction.IsValid())
{
return FReply::Unhandled();
}
if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId())
{
FEdGraphSchemaAction_K2Graph* FuncAction = (FEdGraphSchemaAction_K2Graph*)InAction.Get();
if (URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(FuncAction->EdGraph))
{
if (URigVMBlueprint* RigBlueprint = Cast<URigVMBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph)))
{
TSharedRef<FRigVMFunctionDragDropAction> Action = FRigVMFunctionDragDropAction::New(InAction, RigBlueprint, RigGraph);
Action->SetAltDrag(MouseEvent.IsAltDown());
Action->SetCtrlDrag(MouseEvent.IsControlDown());
return FReply::Handled().BeginDragDrop(Action);
}
}
}
else if(InAction->GetTypeId() == FRigVMEdGraphSchemaAction_LocalVar::StaticGetTypeId())
{
FRigVMEdGraphSchemaAction_LocalVar* VarAction = (FRigVMEdGraphSchemaAction_LocalVar*)InAction.Get();
if (URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(VarAction->GetVariableScope()))
{
if (URigVMBlueprint* RigBlueprint = Cast<URigVMBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph)))
{
TSharedRef<FRigVMFunctionDragDropAction> Action = FRigVMFunctionDragDropAction::New(InAction, RigBlueprint, RigGraph);
Action->SetAltDrag(MouseEvent.IsAltDown());
Action->SetCtrlDrag(MouseEvent.IsControlDown());
return FReply::Handled().BeginDragDrop(Action);
}
}
}
return FReply::Unhandled();
}
FConnectionDrawingPolicy* URigVMEdGraphSchema::CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const
{
#if WITH_EDITOR
if (const URigVMBlueprint* Blueprint = Cast<URigVMBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(InGraphObj)))
{
return Blueprint->GetEditorModule()->CreateConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements, InGraphObj);
}
return IRigVMEditorModule::Get().CreateConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements, InGraphObj);
#else
check(0);
return nullptr;
#endif
}
bool URigVMEdGraphSchema::ShouldHidePinDefaultValue(UEdGraphPin* Pin) const
{
// we should hide default values if any of our parents are connected
if(HasParentConnection_Recursive(Pin))
{
return true;
}
if(const URigVMEdGraphNode* RigGraphNode = Cast<URigVMEdGraphNode>(Pin->GetOwningNode()))
{
if(RigGraphNode->DrawAsCompactNode())
{
return true;
}
}
return false;
}
bool URigVMEdGraphSchema::IsPinBeingWatched(UEdGraphPin const* Pin) const
{
if (const URigVMEdGraphNode* Node = Cast< URigVMEdGraphNode>(Pin->GetOwningNode()))
{
if (const URigVMPin* ModelPin = Node->GetModel()->FindPin(Pin->GetName()))
{
return ModelPin->RequiresWatch();
}
}
return false;
}
void URigVMEdGraphSchema::ClearPinWatch(UEdGraphPin const* Pin) const
{
if (const URigVMEdGraphNode* Node = Cast< URigVMEdGraphNode>(Pin->GetOwningNode()))
{
Node->GetController()->SetPinIsWatched(Pin->GetName(), false);
}
}
void URigVMEdGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2f& GraphPosition) const
{
if (const URigVMEdGraphNode* Node = Cast< URigVMEdGraphNode>(PinA->GetOwningNode()))
{
if (URigVMLink* Link = Node->GetModel()->FindLink(FString::Printf(TEXT("%s -> %s"), *PinA->GetName(), *PinB->GetName())))
{
Node->GetController()->AddRerouteNodeOnLink(Link, FDeprecateSlateVector2D(GraphPosition), FString(), true, true);
}
}
}
bool URigVMEdGraphSchema::MarkBlueprintDirtyFromNewNode(UBlueprint* InBlueprint, UEdGraphNode* InEdGraphNode) const
{
if (InBlueprint == nullptr || InEdGraphNode == nullptr)
{
return false;
}
return true;
}
bool URigVMEdGraphSchema::IsStructEditable(UStruct* InStruct) const
{
if (InStruct == TBaseStructure<FQuat>::Get())
{
return true;
}
if (InStruct == FRuntimeFloatCurve::StaticStruct())
{
return true;
}
return false;
}
void URigVMEdGraphSchema::SetNodePosition(UEdGraphNode* Node, const FVector2f& Position) const
{
return SetNodePosition(Node, FVector2D(Position), true);
}
void URigVMEdGraphSchema::SetNodePosition(UEdGraphNode* Node, const FVector2D& Position, bool bSetupUndo) const
{
StartGraphNodeInteraction(Node);
if (const URigVMEdGraphNode* RigNode = Cast<URigVMEdGraphNode>(Node))
{
RigNode->GetController()->SetNodePosition(RigNode->GetModelNode(), Position, bSetupUndo, false, false);
}
if (const UEdGraphNode_Comment* CommentNode = Cast<UEdGraphNode_Comment>(Node))
{
if(const URigVMEdGraph* Graph = CommentNode->GetTypedOuter<URigVMEdGraph>())
{
Graph->GetController()->SetNodePositionByName(CommentNode->GetFName(), Position, bSetupUndo, false, false);
}
}
}
void URigVMEdGraphSchema::GetGraphDisplayInformation(const UEdGraph& Graph, /*out*/ FGraphDisplayInfo& DisplayInfo) const
{
Super::GetGraphDisplayInformation(Graph, DisplayInfo);
if (const URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>((UEdGraph*)&Graph))
{
if(const URigVMGraph* Model = RigGraph->GetModel())
{
TArray<FString> NodePathParts;
if (URigVMNode::SplitNodePath(RigGraph->ModelNodePath, NodePathParts))
{
if(NodePathParts.Num() > 1)
{
DisplayInfo.DisplayName = FText::FromString(NodePathParts.Last());
DisplayInfo.PlainName = DisplayInfo.DisplayName;
static const FText LocalFunctionText = FText::FromString(TEXT("A local function."));
DisplayInfo.Tooltip = LocalFunctionText;
}
// if this is a riggraph within a collapse node - let's use that for the tooltip
if(const URigVMCollapseNode* CollapseNode = Model->GetTypedOuter<URigVMCollapseNode>())
{
DisplayInfo.Tooltip = CollapseNode->GetToolTipText();
}
}
if(Model->IsRootGraph())
{
// let's see if there is only one event
FString EventName;
if(Algo::CountIf(Model->GetNodes(), [&EventName](const URigVMNode* NodeToCount) -> bool
{
if(NodeToCount->IsEvent() && NodeToCount->CanOnlyExistOnce())
{
if(EventName.IsEmpty())
{
if(const URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(NodeToCount))
{
if(UnitNode->GetScriptStruct())
{
EventName = UnitNode->GetScriptStruct()->GetDisplayNameText().ToString();
}
}
}
return true;
}
return false;
}) == 1)
{
static constexpr TCHAR EventGraphNameFormat[] = TEXT("%s Graph");
const FString DesiredGraphName = FString::Printf(EventGraphNameFormat, *EventName);
DisplayInfo.DisplayName = FText::FromString(DesiredGraphName);
DisplayInfo.PlainName = DisplayInfo.DisplayName;
}
else
{
static const FText MainGraphText = FText::FromString(TEXT("A top level graph for the rig."));
DisplayInfo.Tooltip = MainGraphText;
if(!NodePathParts.IsEmpty())
{
static constexpr TCHAR NodePathSuffix[] = TEXT("::");
FString NodePath = NodePathParts[0];
NodePath.RemoveFromEnd(NodePathSuffix);
NodePath.RemoveFromStart(FRigVMClient::RigVMModelPrefix);
NodePath.TrimStartAndEndInline();
DisplayInfo.DisplayName = FText::FromString(NodePath);
DisplayInfo.PlainName = DisplayInfo.DisplayName;
}
}
}
}
}
}
bool URigVMEdGraphSchema::GetLocalVariables(const UEdGraph* InGraph, TArray<FBPVariableDescription>& OutLocalVariables) const
{
OutLocalVariables.Reset();
if (const URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>((UEdGraph*)InGraph))
{
if (const URigVMGraph* Model = RigGraph->GetModel())
{
TArray<FRigVMGraphVariableDescription> LocalVariables = Model->GetLocalVariables();
for (FRigVMGraphVariableDescription LocalVariable : LocalVariables)
{
FBPVariableDescription VariableDescription;
VariableDescription.VarName = LocalVariable.Name;
VariableDescription.FriendlyName = LocalVariable.Name.ToString();
VariableDescription.DefaultValue = LocalVariable.DefaultValue;
VariableDescription.VarType = LocalVariable.ToPinType();
VariableDescription.PropertyFlags |= CPF_BlueprintVisible;
OutLocalVariables.Add(VariableDescription);
}
}
}
return true;
}
TSharedPtr<FEdGraphSchemaAction> URigVMEdGraphSchema::MakeActionFromVariableDescription(const UEdGraph* InEdGraph,
const FBPVariableDescription& Variable) const
{
if (const URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>((UEdGraph*)InEdGraph))
{
FText Category = Variable.Category;
if (Variable.Category.EqualTo(UEdGraphSchema_K2::VR_DefaultCategory))
{
Category = FText::GetEmpty();
}
#if WITH_RIGVMLEGACYEDITOR
TSharedPtr<FRigVMEdGraphSchemaAction_LocalVar> Action = MakeShareable(new FRigVMEdGraphSchemaAction_LocalVar(Category, FText::FromName(Variable.VarName), FText::GetEmpty(), 0, NodeSectionID::LOCAL_VARIABLE));
#else
TSharedPtr<FRigVMEdGraphSchemaAction_LocalVar> Action = MakeShareable(new FRigVMEdGraphSchemaAction_LocalVar(Category, FText::FromName(Variable.VarName), FText::GetEmpty(), 0, RigVMNodeSectionID::LOCAL_VARIABLE));
#endif
Action->SetVariableInfo(Variable.VarName, RigGraph, Variable.VarType.PinCategory == UEdGraphSchema_K2::PC_Boolean);
return Action;
}
return nullptr;
}
FText URigVMEdGraphSchema::GetGraphCategory(const UEdGraph* InGraph) const
{
if (const URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>((UEdGraph*)InGraph))
{
if (const URigVMGraph* Model = RigGraph->GetModel())
{
if (const URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(Model->GetOuter()))
{
return FText::FromString(CollapseNode->GetNodeCategory());
}
}
}
return FText();
}
EGraphType URigVMEdGraphSchema::GetGraphType(const UEdGraph* TestEdGraph) const
{
if (const URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>((UEdGraph*)TestEdGraph))
{
if (Cast<URigVMBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph)))
{
if (const URigVMGraph* Model = RigGraph->GetModel())
{
if(Model->IsRootGraph())
{
if(Model->IsA<URigVMFunctionLibrary>())
{
return EGraphType::GT_Function;
}
return EGraphType::GT_Ubergraph;
}
if(const URigVMLibraryNode* LibraryNode = Cast<URigVMLibraryNode>(Model->GetOuter()))
{
if(LibraryNode->GetGraph()->IsA<URigVMFunctionLibrary>())
{
return EGraphType::GT_Function;
}
// collapse nodes show up as uber graphs
return EGraphType::GT_Ubergraph;
}
}
}
}
return Super::GetGraphType(TestEdGraph);
}
FReply URigVMEdGraphSchema::TrySetGraphCategory(const UEdGraph* InGraph, const FText& InCategory) const
{
if (const URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>((UEdGraph*)InGraph))
{
if (URigVMBlueprint* RigBlueprint = Cast<URigVMBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph)))
{
if (const URigVMGraph* Model = RigGraph->GetModel())
{
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(Model->GetOuter()))
{
if (URigVMController* Controller = RigBlueprint->GetOrCreateController(CollapseNode->GetGraph()))
{
if (Controller->SetNodeCategory(CollapseNode, InCategory.ToString(), true, false, true))
{
return FReply::Handled();
}
}
}
}
}
}
return FReply::Unhandled();
}
bool URigVMEdGraphSchema::TryDeleteGraph(UEdGraph* GraphToDelete) const
{
if (const URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(GraphToDelete))
{
if (URigVMBlueprint* RigBlueprint = Cast<URigVMBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph)))
{
if (const URigVMGraph* Model = RigBlueprint->GetModel(GraphToDelete))
{
if(Model->IsRootGraph())
{
RigBlueprint->RemoveModel(Model->GetNodePath());
return true;
}
else if (URigVMCollapseNode* LibraryNode = Cast<URigVMCollapseNode>(Model->GetOuter()))
{
if (URigVMController* Controller = RigBlueprint->GetOrCreateController(LibraryNode->GetGraph()))
{
// check if there is a "bulk remove function" transaction going on.
// which implies that a category is being deleted
if (GEditor->CanTransact())
{
if (GEditor->Trans->GetQueueLength() > 0)
{
const FTransaction* LastTransaction = GEditor->Trans->GetTransaction(GEditor->Trans->GetQueueLength() - 1);
if (LastTransaction)
{
if (LastTransaction->GetTitle().ToString() == TEXT("Bulk Remove Functions"))
{
// instead of deleting the graph, let's set its category to none
// and thus moving it to the top of the tree
return Controller->SetNodeCategory(LibraryNode, FString());
}
}
}
}
bool bSetupUndoRedo = true;
// if the element to remove is a function, check if it is public and referenced. If so,
// warn the user about a bulk remove
if (URigVMFunctionLibrary* Library = Cast<URigVMFunctionLibrary>(LibraryNode->GetGraph()))
{
const FName& FunctionName = LibraryNode->GetFName();
if (RigBlueprint->IsFunctionPublic(FunctionName))
{
for (auto Reference : Library->GetReferencesForFunction(FunctionName))
{
if (Reference.IsValid())
{
const URigVMBlueprint* OtherBlueprint = Reference->GetTypedOuter<URigVMBlueprint>();
if (OtherBlueprint != RigBlueprint)
{
if(RigBlueprint->OnRequestBulkEditDialog().IsBound())
{
URigVMController* FunctionController = RigBlueprint->GetController(LibraryNode->GetContainedGraph());
const FRigVMController_BulkEditResult Result = RigBlueprint->OnRequestBulkEditDialog().Execute(RigBlueprint, FunctionController, LibraryNode, ERigVMControllerBulkEditType::RemoveFunction);
if(Result.bCanceled)
{
return false;
}
bSetupUndoRedo = Result.bSetupUndoRedo;
}
break;
}
}
}
}
}
return Controller->RemoveNode(LibraryNode, bSetupUndoRedo, false);
}
}
}
}
}
return false;
}
bool URigVMEdGraphSchema::TryRenameGraph(UEdGraph* GraphToRename, const FName& InNewName) const
{
if (const URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(GraphToRename))
{
if (IRigVMClientHost* ClientHost = RigGraph->GetImplementingOuter<IRigVMClientHost>())
{
if (const URigVMGraph* Model = RigGraph->GetModel())
{
if(Model->IsRootGraph())
{
const FString NewName = FString::Printf(TEXT("%s %s"), FRigVMClient::RigVMModelPrefix, *InNewName.ToString());
ClientHost->RenameGraph(Model->GetNodePath(), *NewName);
}
else if (const URigVMGraph* RootModel = Model->GetRootGraph())
{
URigVMLibraryNode* LibraryNode = Cast<URigVMLibraryNode>(RootModel->FindNode(RigGraph->ModelNodePath));
if (LibraryNode)
{
if (URigVMController* Controller = ClientHost->GetOrCreateController(LibraryNode->GetGraph()))
{
Controller->RenameNode(LibraryNode, InNewName, true, true);
return true;
}
}
}
}
}
}
return false;
}
bool URigVMEdGraphSchema::TryToGetChildEvents(const UEdGraph* Graph, const int32 SectionId,
TArray<TSharedPtr<FEdGraphSchemaAction>>& Actions, const FText& ParentCategory) const
{
check(Graph);
if(const URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(Graph))
{
if(const URigVMGraph* Model = RigGraph->GetModel())
{
TArray<FName> EventNames;
TMap<FName, const URigVMNode*> EventNameToNode;
for(const URigVMNode* Node : Model->GetNodes())
{
if(Node->IsEvent())
{
if(!EventNames.Contains(Node->GetEventName()))
{
EventNames.Add(Node->GetEventName());
EventNameToNode.Add(Node->GetEventName(), Node);
}
}
}
if(!EventNames.IsEmpty())
{
FGraphDisplayInfo EdGraphDisplayInfo;
GetGraphDisplayInformation(*Graph, EdGraphDisplayInfo);
FText EdGraphDisplayName = EdGraphDisplayInfo.DisplayName;
FText ActionCategory;
if (!ParentCategory.IsEmpty())
{
ActionCategory = FText::Format(FText::FromString(TEXT("{0}|{1}")), ParentCategory, EdGraphDisplayName);
}
else
{
ActionCategory = MoveTemp(EdGraphDisplayName);
}
for(const FName& EventName : EventNames)
{
const URigVMNode* Node = EventNameToNode.FindChecked(EventName);
TSharedPtr<FEdGraphSchemaAction> Action = MakeShareable(
new FRigVMEdGraphSchemaAction_Event(EventName, Node->GetNodePath(true), ActionCategory)
);
Action->SectionID = SectionId;
Actions.Add(Action);
}
}
return true;
}
}
return false;
}
UEdGraphPin* URigVMEdGraphSchema::DropPinOnNode(UEdGraphNode* InTargetNode, const FName& InSourcePinName, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection) const
{
if (URigVMBlueprint* RigBlueprint = Cast<URigVMBlueprint>(FBlueprintEditorUtils::FindBlueprintForNode(InTargetNode)))
{
if (URigVMEdGraphNode* RigNode = Cast<URigVMEdGraphNode>(InTargetNode))
{
if (URigVMNode* ModelNode = RigNode->GetModelNode())
{
FString NewPinName;
const URigVMGraph* Model = nullptr;
ERigVMPinDirection PinDirection = InSourcePinDirection == EGPD_Input ? ERigVMPinDirection::Input : ERigVMPinDirection::Output;
if (const URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(ModelNode))
{
Model = CollapseNode->GetContainedGraph();
PinDirection = PinDirection == ERigVMPinDirection::Output ? ERigVMPinDirection::Input : ERigVMPinDirection::Output;
}
else if (ModelNode->IsA<URigVMFunctionInterfaceNode>())
{
Model = ModelNode->GetGraph();
}
if (Model)
{
ensure(!Model->IsTopLevelGraph());
const FRigVMExternalVariable ExternalVar = RigVMTypeUtils::ExternalVariableFromPinType(InSourcePinName, InSourcePinType);
if (ExternalVar.IsValid(true /* allow null memory */))
{
if (URigVMController* Controller = RigBlueprint->GetController(Model))
{
FName SourcePinName = InSourcePinName;
FString TypeName = ExternalVar.TypeName.ToString();
if (ExternalVar.bIsArray)
{
TypeName = RigVMTypeUtils::ArrayTypeFromBaseType(*TypeName);
}
FName TypeObjectPathName = NAME_None;
if (ExternalVar.TypeObject)
{
TypeObjectPathName = *ExternalVar.TypeObject->GetPathName();
if(const UScriptStruct* CPPTypeStruct = Cast<UScriptStruct>(ExternalVar.TypeObject))
{
if(CPPTypeStruct->IsChildOf(FRigVMExecutePin::StaticStruct()))
{
SourcePinName = FRigVMStruct::ExecuteContextName;
PinDirection = ERigVMPinDirection::IO;
}
}
}
FString DefaultValue;
if (PinBeingDropped)
{
if (const URigVMEdGraphNode* SourceNode = Cast<URigVMEdGraphNode>(PinBeingDropped->GetOwningNode()))
{
if (const URigVMPin* SourcePin = SourceNode->GetModelPinFromPinPath(PinBeingDropped->GetName()))
{
DefaultValue = SourcePin->GetDefaultValue();
}
}
}
const FName ExposedPinName = Controller->AddExposedPin(
SourcePinName,
PinDirection,
TypeName,
TypeObjectPathName,
DefaultValue,
true,
true
);
if (!ExposedPinName.IsNone())
{
NewPinName = ExposedPinName.ToString();
}
}
}
}
if (!NewPinName.IsEmpty())
{
if (const URigVMPin* NewModelPin = ModelNode->FindPin(NewPinName))
{
return RigNode->FindPin(*NewModelPin->GetPinPath());
}
}
}
}
}
return nullptr;
}
bool URigVMEdGraphSchema::SupportsDropPinOnNode(UEdGraphNode* InTargetNode, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection, FText& OutErrorMessage) const
{
if (const URigVMEdGraphNode* RigNode = Cast<URigVMEdGraphNode>(InTargetNode))
{
if(const URigVMNode* ModelNode = RigNode->GetModelNode())
{
if (ModelNode->IsA<URigVMFunctionEntryNode>())
{
if (InSourcePinDirection == EGPD_Output)
{
OutErrorMessage = LOCTEXT("AddPinToReturnNode", "Add Pin to Return Node");
return false;
}
return true;
}
else if (ModelNode->IsA<URigVMFunctionReturnNode>())
{
if (InSourcePinDirection == EGPD_Input)
{
OutErrorMessage = LOCTEXT("AddPinToEntryNode", "Add Pin to Entry Node");
return false;
}
return true;
}
else if (ModelNode->IsA<URigVMCollapseNode>())
{
return true;
}
}
}
return false;
}
URigVMEdGraphNode* URigVMEdGraphSchema::CreateGraphNode(URigVMEdGraph* InGraph, const FName& InPropertyName) const
{
const bool bSelectNewNode = true;
FGraphNodeCreator<URigVMEdGraphNode> GraphNodeCreator(*InGraph);
URigVMEdGraphNode* EdGraphNode = GraphNodeCreator.CreateNode(bSelectNewNode, GetGraphNodeClass(InGraph));
EdGraphNode->ModelNodePath = InPropertyName.ToString();
GraphNodeCreator.Finalize();
return EdGraphNode;
}
void URigVMEdGraphSchema::TrySetDefaultValue(UEdGraphPin& InPin, const FString& InNewDefaultValue, bool bMarkAsModified) const
{
#if WITH_EDITOR
if (GEditor)
{
GEditor->CancelTransaction(0);
}
#endif
GetDefault<UEdGraphSchema_K2>()->TrySetDefaultValue(InPin, InNewDefaultValue, false);
}
void URigVMEdGraphSchema::TrySetDefaultObject(UEdGraphPin& InPin, UObject* InNewDefaultObject, bool bMarkAsModified) const
{
#if WITH_EDITOR
if (GEditor)
{
GEditor->CancelTransaction(0);
}
#endif
GetDefault<UEdGraphSchema_K2>()->TrySetDefaultObject(InPin, InNewDefaultObject, false);
}
void URigVMEdGraphSchema::TrySetDefaultText(UEdGraphPin& InPin, const FText& InNewDefaultText, bool bMarkAsModified) const
{
#if WITH_EDITOR
if (GEditor)
{
GEditor->CancelTransaction(0);
}
#endif
GetDefault<UEdGraphSchema_K2>()->TrySetDefaultText(InPin, InNewDefaultText, false);
}
bool URigVMEdGraphSchema::ArePinsCompatible(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UClass* CallingContext, bool bIgnoreArray /*= false*/) const
{
// filter out pins which have a parent
if (PinB->ParentPin != nullptr)
{
return false;
}
// if we are looking at a polymorphic node
if((PinA->PinType.ContainerType == PinB->PinType.ContainerType) ||
(PinA->PinType.PinSubCategoryObject != PinB->PinType.PinSubCategoryObject))
{
auto IsPinCompatibleWithType = [](const UEdGraphPin* InPin, const FEdGraphPinType& InPinType) -> bool
{
if(const URigVMEdGraphNode* RigNode = Cast<URigVMEdGraphNode>(InPin->GetOwningNode()))
{
FString CPPType;
UObject* CPPTypeObject = nullptr;
if(RigVMTypeUtils::CPPTypeFromPinType(InPinType, CPPType, &CPPTypeObject))
{
FString PinPath, PinName;
URigVMPin::SplitPinPathAtEnd(InPin->GetName(), PinPath, PinName);
if((InPin->ParentPin != nullptr) &&
(InPin->ParentPin->ParentPin == nullptr) &&
(InPin->ParentPin->PinType.ContainerType == EPinContainerType::Array))
{
URigVMPin::SplitPinPathAtEnd(InPin->ParentPin->GetName(), PinPath, PinName);
CPPType = RigVMTypeUtils::ArrayTypeFromBaseType(CPPType);
}
const FRigVMTemplateArgumentType Type(*CPPType, CPPTypeObject);
const TRigVMTypeIndex TypeIndex = FRigVMRegistry::Get().GetTypeIndex(Type);
if(const FRigVMTemplate* Template = RigNode->GetTemplate())
{
if(const FRigVMTemplateArgument* Argument = Template->FindArgument(*PinName))
{
if(Argument->SupportsTypeIndex(TypeIndex))
{
return true;
}
const TArray<TRigVMTypeIndex>& AvailableCasts = RigVMTypeUtils::GetAvailableCasts(TypeIndex, InPin->Direction == EGPD_Output);
for(const TRigVMTypeIndex& AvailableCast : AvailableCasts)
{
if(Argument->SupportsTypeIndex(AvailableCast))
{
return true;
}
}
}
}
}
}
return false;
};
auto IsTemplateNodePin = [](const UEdGraphPin* InPin)
{
if(const URigVMEdGraphNode* RigNode = Cast<URigVMEdGraphNode>(InPin->GetOwningNode()))
{
return RigNode->GetTemplate() != nullptr;
};
return false;
};
if(PinA->PinType.PinSubCategoryObject == RigVMTypeUtils::GetWildCardCPPTypeObject() || IsTemplateNodePin(PinA))
{
if(IsPinCompatibleWithType(PinA, PinB->PinType))
{
URigVMEdGraphSchema* MutableThis = (URigVMEdGraphSchema*)this;
MutableThis->LastPinForCompatibleCheck = PinB;
MutableThis->bLastPinWasInput = PinB->Direction == EGPD_Input;
return true;
}
}
if(PinB->PinType.PinSubCategoryObject == RigVMTypeUtils::GetWildCardCPPTypeObject() || IsTemplateNodePin(PinB))
{
if(IsPinCompatibleWithType(PinB, PinA->PinType))
{
URigVMEdGraphSchema* MutableThis = (URigVMEdGraphSchema*)this;
MutableThis->LastPinForCompatibleCheck = PinA;
MutableThis->bLastPinWasInput = PinA->Direction == EGPD_Input;
return true;
}
}
}
// for large world coordinate support we should allow connections
// between float and double
if(PinA->PinType.ContainerType == EPinContainerType::None &&
PinB->PinType.ContainerType == EPinContainerType::None)
{
if((PinA->PinType.PinCategory == UEdGraphSchema_K2::PC_Float &&
PinB->PinType.PinCategory == UEdGraphSchema_K2::PC_Double) ||
(PinA->PinType.PinCategory == UEdGraphSchema_K2::PC_Double &&
PinB->PinType.PinCategory == UEdGraphSchema_K2::PC_Float))
{
return true;
}
}
if(PinA->PinType.ContainerType == EPinContainerType::None &&
PinB->PinType.ContainerType == EPinContainerType::None &&
PinA->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct &&
PinB->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct)
{
if(const UScriptStruct* ScriptStructA = Cast<UScriptStruct>(PinA->PinType.PinSubCategoryObject))
{
if(const UScriptStruct* ScriptStructB = Cast<UScriptStruct>(PinB->PinType.PinSubCategoryObject))
{
if(ScriptStructA->IsChildOf(FRigVMExecutePin::StaticStruct()) &&
ScriptStructB->IsChildOf(FRigVMExecutePin::StaticStruct()))
{
return true;
}
}
}
}
if(GetDefault<UEdGraphSchema_K2>()->ArePinsCompatible(PinA, PinB, CallingContext, bIgnoreArray))
{
return true;
}
// also check if there's a cast available for the type
const TRigVMTypeIndex TypeIndexA = RigVMTypeUtils::TypeIndexFromPinType(PinA->Direction == EGPD_Output ? PinA->PinType : PinB->PinType);
const TRigVMTypeIndex TypeIndexB = RigVMTypeUtils::TypeIndexFromPinType(PinA->Direction == EGPD_Output ? PinB->PinType : PinA->PinType);
return RigVMTypeUtils::CanCastTypes(TypeIndexA, TypeIndexB);
}
void URigVMEdGraphSchema::RenameNode(URigVMEdGraphNode* Node, const FName& InNewNodeName) const
{
Node->NodeTitle = FText::FromName(InNewNodeName);
Node->Modify();
}
void URigVMEdGraphSchema::ResetPinDefaultsRecursive(UEdGraphPin* InPin) const
{
URigVMEdGraphNode* RigNode = Cast<URigVMEdGraphNode>(InPin->GetOwningNode());
if (RigNode == nullptr)
{
return;
}
RigNode->CopyPinDefaultsToModel(InPin);
for (UEdGraphPin* SubPin : InPin->SubPins)
{
ResetPinDefaultsRecursive(SubPin);
}
}
void URigVMEdGraphSchema::GetVariablePinTypes(TArray<FEdGraphPinType>& PinTypes) const
{
PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Boolean, FName(NAME_None), nullptr, EPinContainerType::None, false, FEdGraphTerminalType()));
PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Real, FName(NAME_None), nullptr, EPinContainerType::None, false, FEdGraphTerminalType()));
PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Int, FName(NAME_None), nullptr, EPinContainerType::None, false, FEdGraphTerminalType()));
PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure<FVector>::Get(), EPinContainerType::None, false, FEdGraphTerminalType()));
PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure<FVector2D>::Get(), EPinContainerType::None, false, FEdGraphTerminalType()));
PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure<FRotator>::Get(), EPinContainerType::None, false, FEdGraphTerminalType()));
PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure<FTransform>::Get(), EPinContainerType::None, false, FEdGraphTerminalType()));
PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure<FLinearColor>::Get(), EPinContainerType::None, false, FEdGraphTerminalType()));
}
bool URigVMEdGraphSchema::SafeDeleteNodeFromGraph(UEdGraph* Graph, UEdGraphNode* Node) const
{
if (const URigVMEdGraphNode* RigNode = Cast<URigVMEdGraphNode>(Node))
{
if (GEditor)
{
GEditor->CancelTransaction(0);
}
return RigNode->GetController()->RemoveNode(RigNode->GetModelNode(), true, true);
}
return false;
}
bool URigVMEdGraphSchema::CanVariableBeDropped(UEdGraph* InGraph, FProperty* InVariableToDrop) const
{
const FRigVMExternalVariable ExternalVariable = FRigVMExternalVariable::Make(InVariableToDrop, nullptr);
return ExternalVariable.IsValid(true /* allow nullptr */);
}
bool URigVMEdGraphSchema::RequestFunctionDropOnPanel(UEdGraph* InGraph, const FRigVMGraphFunctionIdentifier& InFunction, const FVector2D& InDropPosition, const FVector2D& InScreenPosition) const
{
if (URigVMBlueprint* TargetRigBlueprint = Cast<URigVMBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(InGraph)))
{
if(URigVMController* TargetController = TargetRigBlueprint->GetController(InGraph))
{
FRigVMGraphFunctionHeader Header = FRigVMGraphFunctionHeader::FindGraphFunctionHeader(InFunction);
if (IRigVMClientHost* SourceClientHost = Cast<IRigVMClientHost>(InFunction.HostObject.ResolveObject()))
{
if (IRigVMGraphFunctionHost* SourceFunctionHost = SourceClientHost->GetRigVMGraphFunctionHost())
{
if (FRigVMGraphFunctionStore* SourceStore = SourceFunctionHost->GetRigVMGraphFunctionStore())
{
#if WITH_EDITOR
if(InFunction.HostObject != TargetRigBlueprint->GetRigVMBlueprintGeneratedClass())
{
if (!SourceStore->IsFunctionPublic(InFunction))
{
TargetRigBlueprint->BroadcastRequestLocalizeFunctionDialog(InFunction);
URigVMLibraryNode* FunctionDefinitionNode = TargetRigBlueprint->GetLocalFunctionLibrary()->FindPreviouslyLocalizedFunction(InFunction);
Header = FunctionDefinitionNode->GetFunctionHeader();
}
}
#endif
}
}
}
return TargetController->AddFunctionReferenceNodeFromDescription(Header, InDropPosition, FString(), true, true) != nullptr;
}
}
return false;
}
bool URigVMEdGraphSchema::RequestVariableDropOnPanel(UEdGraph* InGraph, FProperty* InVariableToDrop, const FVector2f& InDropPosition, const FVector2f& InScreenPosition) const
{
#if WITH_EDITOR
if (CanVariableBeDropped(InGraph, InVariableToDrop))
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(InGraph);
URigVMBlueprint* RigBlueprint = Cast<URigVMBlueprint>(Blueprint);
if (RigBlueprint != nullptr)
{
RigBlueprint->OnVariableDropped().Broadcast(InGraph, InVariableToDrop, FDeprecateSlateVector2D(InDropPosition), FDeprecateSlateVector2D(InScreenPosition));
return true;
}
}
#endif
return false;
}
bool URigVMEdGraphSchema::RequestVariableDropOnPin(UEdGraph* InGraph, FProperty* InVariableToDrop, UEdGraphPin* InPin, const FVector2f& InDropPosition, const FVector2f& InScreenPosition) const
{
#if WITH_EDITOR
if (CanVariableBeDropped(InGraph, InVariableToDrop))
{
if(const URigVMEdGraph* Graph = Cast<URigVMEdGraph>(InGraph))
{
if (const URigVMPin* ModelPin = Graph->GetModel()->FindPin(InPin->GetName()))
{
const FRigVMExternalVariable ExternalVariable = FRigVMExternalVariable::Make(InVariableToDrop, nullptr);
return RequestVariableDropOnPin(InGraph, ExternalVariable, InPin, FDeprecateSlateVector2D(InDropPosition), FDeprecateSlateVector2D(InScreenPosition));
}
}
}
#endif
return false;
}
bool URigVMEdGraphSchema::RequestVariableDropOnPin(UEdGraph* InGraph, const FRigVMExternalVariable& InVariableToDrop, UEdGraphPin* InPin, const FVector2D& InDropPosition, const FVector2D& InScreenPosition) const
{
#if WITH_EDITOR
if (InVariableToDrop.IsValid(true /* allow nullptr */))
{
if(const URigVMEdGraph* Graph = Cast<URigVMEdGraph>(InGraph))
{
if (const URigVMPin* ModelPin = Graph->GetModel()->FindPin(InPin->GetName()))
{
if (ModelPin->CanBeBoundToVariable(InVariableToDrop))
{
const FModifierKeysState KeyState = FSlateApplication::Get().GetModifierKeys();
if (KeyState.IsAltDown())
{
return Graph->GetController()->BindPinToVariable(ModelPin->GetPinPath(), InVariableToDrop.Name.ToString(), true, true);
}
else
{
Graph->GetController()->OpenUndoBracket(TEXT("Bind Variable to Pin"));
if (const URigVMVariableNode* VariableNode = Graph->GetController()->AddVariableNode(InVariableToDrop.Name, InVariableToDrop.TypeName.ToString(), InVariableToDrop.TypeObject, true, FString(), InDropPosition + FVector2D(0.f, -34.f)))
{
Graph->GetController()->AddLink(VariableNode->FindPin(TEXT("Value"))->GetPinPath(), ModelPin->GetPinPath(), true);
}
Graph->GetController()->CloseUndoBracket();
return true;
}
}
}
}
}
#endif
return false;
}
void URigVMEdGraphSchema::StartGraphNodeInteraction(UEdGraphNode* InNode) const
{
#if WITH_EDITOR
check(InNode);
if(NodesBeingInteracted.Contains(InNode))
{
return;
}
NodePositionsDuringStart.Reset();
NodesBeingInteracted.Reset();
const URigVMEdGraph* Graph = Cast<URigVMEdGraph>(InNode->GetOuter());
if (Graph == nullptr)
{
return;
}
check(Graph->GetController());
check(Graph->GetModel());
NodesBeingInteracted = GetNodesToMoveForNode(InNode);
for (const UEdGraphNode* NodeToMove : NodesBeingInteracted)
{
FName NodeName = NodeToMove->GetFName();
if (const URigVMNode* ModelNode = Graph->GetModel()->FindNodeByName(NodeName))
{
NodePositionsDuringStart.FindOrAdd(NodeName, ModelNode->GetPosition());
}
}
#endif
}
void URigVMEdGraphSchema::EndGraphNodeInteraction(UEdGraphNode* InNode) const
{
#if WITH_EDITOR
URigVMEdGraph* Graph = Cast<URigVMEdGraph>(InNode->GetOuter());
if (Graph == nullptr)
{
return;
}
check(Graph->GetController());
check(Graph->GetModel());
TArray<UEdGraphNode*> NodesToMove = GetNodesToMoveForNode(InNode);
bool bMovedSomething = false;
FGuardSkipDirtyBlueprintStatus GuardDirtyBlueprintStatus(Graph->GetBlueprint(), true);
Graph->GetController()->OpenUndoBracket(TEXT("Move Nodes"));
for (const UEdGraphNode* NodeToMove : NodesToMove)
{
FName NodeName = NodeToMove->GetFName();
if (Graph->GetModel()->FindNodeByName(NodeName))
{
FVector2D NewPosition(NodeToMove->NodePosX, NodeToMove->NodePosY);
if(const FVector2D* OldPosition = NodePositionsDuringStart.Find(NodeName))
{
TGuardValue<bool> SuspendNotification(Graph->bSuspendModelNotifications, true);
Graph->GetController()->SetNodePositionByName(NodeName, *OldPosition, false, false);
}
if(Graph->GetController()->SetNodePositionByName(NodeName, NewPosition, true, false, true))
{
bMovedSomething = true;
}
}
}
if (bMovedSomething)
{
if (GEditor)
{
GEditor->CancelTransaction(0);
}
Graph->GetController()->CloseUndoBracket();
}
else
{
Graph->GetController()->CancelUndoBracket();
}
NodesBeingInteracted.Reset();
NodePositionsDuringStart.Reset();
#endif
}
TArray<UEdGraphNode*> URigVMEdGraphSchema::GetNodesToMoveForNode(UEdGraphNode* InNode)
{
TArray<UEdGraphNode*> NodesToMove;
#if WITH_EDITOR
URigVMEdGraph* Graph = Cast<URigVMEdGraph>(InNode->GetOuter());
if (Graph == nullptr)
{
return NodesToMove;
}
NodesToMove.Add(InNode);
for (UEdGraphNode* SelectedGraphNode : Graph->Nodes)
{
if (SelectedGraphNode->IsSelected())
{
NodesToMove.AddUnique(SelectedGraphNode);
}
}
for (int32 NodeIndex = 0; NodeIndex < NodesToMove.Num(); NodeIndex++)
{
if (const UEdGraphNode_Comment* CommentNode = Cast<UEdGraphNode_Comment>(NodesToMove[NodeIndex]))
{
if (CommentNode->MoveMode == ECommentBoxMode::GroupMovement)
{
for (FCommentNodeSet::TConstIterator NodeIt(CommentNode->GetNodesUnderComment()); NodeIt; ++NodeIt)
{
if (UEdGraphNode* NodeUnderComment = Cast<UEdGraphNode>(*NodeIt))
{
NodesToMove.AddUnique(NodeUnderComment);
}
}
}
}
}
#endif
return NodesToMove;
}
FVector2D URigVMEdGraphSchema::GetNodePositionAtStartOfInteraction(const UEdGraphNode* InNode) const
{
#if WITH_EDITOR
if(InNode)
{
if(const FVector2D* Position = NodePositionsDuringStart.Find(InNode->GetFName()))
{
return *Position;
}
return FVector2D(InNode->NodePosX, InNode->NodePosY);
}
#endif
return FVector2D::ZeroVector;
}
bool URigVMEdGraphSchema::AutowireNewNode(URigVMEdGraphNode* NewNode, UEdGraphPin* FromPin) const
{
// copying high level information into a local array since the try create connection below
// may cause the pin array to be destroyed / changed
TArray<TPair<FName, EEdGraphPinDirection>> PinsToVisit;
for(UEdGraphPin* Pin : NewNode->Pins)
{
PinsToVisit.Emplace(Pin->GetFName(), Pin->Direction);
}
TArray<UEdGraphPin*> OldLinkedTo = FromPin->LinkedTo;
for(const TPair<FName, EEdGraphPinDirection>& PinToVisit : PinsToVisit)
{
UEdGraphPin* Pin = NewNode->FindPin(PinToVisit.Key, PinToVisit.Value);
if(Pin == nullptr)
{
continue;
}
if (Pin->ParentPin != nullptr)
{
continue;
}
FPinConnectionResponse ConnectResponse = CanCreateConnection(FromPin, Pin);
if(ConnectResponse.Response != ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW)
{
if (TryCreateConnection(FromPin, Pin))
{
// It might have been linked in a different direction. Find pin with the correct direction
if (Pin->LinkedTo.IsEmpty())
{
for (UEdGraphPin* Linked : FromPin->LinkedTo)
{
if (Linked->GetOwningNode() == Pin->GetOwningNode())
{
Pin = Linked;
break;
}
}
}
// If the pin is an execute context, try to create a link to the pin which was previously connected to FromPin
if (const URigVMPin* ModelPin = NewNode->FindModelPinFromGraphPin(Pin))
{
if (!OldLinkedTo.IsEmpty() && ModelPin->IsExecuteContext())
{
const bool bIsInput = Pin->Direction == EEdGraphPinDirection::EGPD_Input;
// If the pin is an input to a control flow node, the output used should be the completed pin
if (ModelPin->GetNode()->IsControlFlowNode())
{
if (bIsInput)
{
ModelPin = ModelPin->GetNode()->FindPin(FRigVMStruct::ControlFlowCompletedName.ToString());
}
}
// Try to create the second connection
if (UEdGraphPin* OppositePin = NewNode->FindGraphPinFromModelPin(ModelPin, !bIsInput))
{
if (CanCreateConnection(OppositePin, OldLinkedTo[0]).Response != ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW)
{
TryCreateConnection(OppositePin, OldLinkedTo[0]);
}
}
}
// copy the default value over if the node is a reroute,
// a make array, make struct or make constant
if(!ModelPin->IsExecuteContext() && FromPin->Direction == EGPD_Input)
{
if(const URigVMPin* OtherModelPin = ModelPin->GetGraph()->FindPin(FromPin->GetName()))
{
FString PinNameToSet;
if(ModelPin->GetNode()->IsA<URigVMRerouteNode>())
{
PinNameToSet = TEXT("Value");
}
else if(const URigVMDispatchNode* DispatchNode = Cast<URigVMDispatchNode>(ModelPin->GetNode()))
{
if(const FRigVMDispatchFactory* Factory = DispatchNode->GetFactory())
{
if(Factory->GetFactoryName() == FRigVMDispatch_Constant().GetFactoryName())
{
PinNameToSet = TEXT("Value");
}
else if(Factory->GetFactoryName() == FRigVMDispatch_MakeStruct().GetFactoryName())
{
PinNameToSet = TEXT("Elements");
}
else if(Factory->GetFactoryName() == FRigVMDispatch_ArrayMake().GetFactoryName())
{
PinNameToSet = TEXT("Values");
}
}
}
if(!PinNameToSet.IsEmpty())
{
const FString DefaultValue = OtherModelPin->GetDefaultValue();
if(!DefaultValue.IsEmpty())
{
NewNode->GetController()->SetPinDefaultValue(RigVMStringUtils::JoinPinPath(NewNode->GetName(), PinNameToSet), DefaultValue, true, true, false, true);
}
}
}
}
}
return true;
}
}
}
return false;
}
void URigVMEdGraphSchema::HandleModifiedEvent(ERigVMGraphNotifType InNotifType, URigVMGraph* InGraph,
UObject* InSubject)
{
switch(InNotifType)
{
case ERigVMGraphNotifType::NodeAdded:
case ERigVMGraphNotifType::NodeRemoved:
case ERigVMGraphNotifType::PinAdded:
case ERigVMGraphNotifType::PinRemoved:
case ERigVMGraphNotifType::PinRenamed:
case ERigVMGraphNotifType::PinArraySizeChanged:
case ERigVMGraphNotifType::PinTypeChanged:
case ERigVMGraphNotifType::LinkAdded:
case ERigVMGraphNotifType::LinkRemoved:
{
LastPinForCompatibleCheck = nullptr;
break;
}
default:
{
break;
}
}
}
TSubclassOf<URigVMEdGraphNode> URigVMEdGraphSchema::GetGraphNodeClass(const URigVMEdGraph* InGraph) const
{
if (const UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(InGraph))
{
if (const URigVMBlueprint* RigBlueprint = CastChecked<URigVMBlueprint>(Blueprint))
{
return RigBlueprint->GetRigVMEdGraphNodeClass();
}
}
return nullptr;
}
bool URigVMEdGraphSchema::IsRigVMDefaultEvent(const FName& InEventName) const
{
return false;
}
#undef LOCTEXT_NAMESPACE