Files
UnrealEngine/Engine/Source/Editor/Kismet/Private/BPVariableDragDropAction.cpp
2025-05-18 13:04:45 +08:00

586 lines
21 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BPVariableDragDropAction.h"
#include "BlueprintEditor.h"
#include "Containers/EnumAsByte.h"
#include "Delegates/Delegate.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphNode.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraph/EdGraphSchema.h"
#include "EdGraphSchema_K2.h"
#include "EdGraphSchema_K2_Actions.h"
#include "Engine/Blueprint.h"
#include "Framework/Application/MenuStack.h"
#include "Framework/Application/SlateApplication.h"
#include "Framework/Commands/UIAction.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "GenericPlatform/GenericApplication.h"
#include "HAL/PlatformCrt.h"
#include "HAL/PlatformMath.h"
#include "Internationalization/Internationalization.h"
#include "Internationalization/Text.h"
#include "K2Node_Variable.h"
#include "K2Node_VariableGet.h"
#include "K2Node_VariableSet.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Layout/WidgetPath.h"
#include "Misc/AssertionMacros.h"
#include "ScopedTransaction.h"
#include "Templates/Casts.h"
#include "Textures/SlateIcon.h"
#include "UObject/ObjectMacros.h"
#include "UObject/ObjectPtr.h"
#include "UObject/UObjectGlobals.h"
class SWidget;
struct FSlateBrush;
struct FSlateColor;
#define LOCTEXT_NAMESPACE "VariableDragDropAction"
FKismetVariableDragDropAction::FKismetVariableDragDropAction()
: VariableName(NAME_None)
{
}
UBlueprint* FKismetVariableDragDropAction::GetSourceBlueprint() const
{
check(VariableSource.IsValid());
UClass* VariableSourceClass = nullptr;
if (VariableSource.Get()->IsA(UClass::StaticClass()))
{
VariableSourceClass = CastChecked<UClass>(VariableSource.Get());
}
else
{
check(VariableSource.Get()->GetOuter());
VariableSourceClass = CastChecked<UClass>(VariableSource.Get()->GetOuter());
}
return UBlueprint::GetBlueprintFromClass(VariableSourceClass);
}
void FKismetVariableDragDropAction::GetLinksThatWillBreak( UEdGraphNode* Node, FProperty* NewVariableProperty,
TArray<class UEdGraphPin*>& OutBroken)
{
if(UK2Node_Variable* VarNodeUnderCursor = Cast<UK2Node_Variable>(Node))
{
if(const UEdGraphSchema_K2* Schema = Cast<const UEdGraphSchema_K2>(VarNodeUnderCursor->GetSchema()) )
{
FEdGraphPinType NewPinType;
Schema->ConvertPropertyToPinType(NewVariableProperty,NewPinType);
if(UEdGraphPin* Pin = VarNodeUnderCursor->FindPin(VarNodeUnderCursor->GetVarName()))
{
for(TArray<class UEdGraphPin*>::TIterator i(Pin->LinkedTo);i;i++)
{
UEdGraphPin* Link = *i;
if(false == Schema->ArePinTypesCompatible(NewPinType, Link->PinType))
{
OutBroken.Add(Link);
}
}
}
}
}
}
namespace
{
/**
* Helper function to determine if a node has any split pins on it.
*
* @return True if there is a split pin on the node
*/
static bool NodeHasSplitPins(UEdGraphNode* InNode)
{
if (InNode)
{
for (UEdGraphPin* Pin : InNode->Pins)
{
// If a pin has no parent node but has SubPins then it is a split pin
if (Pin && !Pin->ParentPin && Pin->SubPins.Num() > 0)
{
return true;
}
}
}
return false;
}
}
void FKismetVariableDragDropAction::HoverTargetChanged()
{
FProperty* VariableProperty = GetVariableProperty();
if (VariableProperty == nullptr)
{
return;
}
FString VariableString = VariableName.ToString();
// Icon/text to draw on tooltip
FText Message = LOCTEXT("InvalidDropTarget", "Invalid drop target!");
UEdGraphPin* PinUnderCursor = GetHoveredPin();
bool bCanMakeSetter = true;
bool bBadSchema = false;
bool bBadGraph = false;
UEdGraph* TheHoveredGraph = GetHoveredGraph();
if (TheHoveredGraph)
{
if (!TheHoveredGraph->GetSchema()->CanVariableBeDropped(TheHoveredGraph, VariableProperty))
{
bBadSchema = true;
}
else if (!CanVariableBeDropped(VariableProperty, *TheHoveredGraph))
{
bBadGraph = true;
}
UStruct* Outer = VariableProperty->GetOwnerChecked<UStruct>();
FNodeConstructionParams NewNodeParams;
NewNodeParams.VariableName = VariableName;
const UBlueprint* DropOnBlueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TheHoveredGraph);
NewNodeParams.Graph = TheHoveredGraph;
NewNodeParams.VariableSource = Outer;
bCanMakeSetter = CanExecuteMakeSetter(NewNodeParams, VariableProperty);
}
UEdGraphNode* VarNodeUnderCursor = Cast<UK2Node_Variable>(GetHoveredNode());
if (bBadSchema)
{
SetFeedbackMessageError(LOCTEXT("CannotCreateInThisSchema", "Cannot access variables in this type of graph"));
}
else if (bBadGraph)
{
FFormatNamedArguments Args;
Args.Add(TEXT("VariableName"), FText::FromString(VariableString));
Args.Add(TEXT("Scope"), FText::FromString(TheHoveredGraph->GetName()));
if (IsFromBlueprint(FBlueprintEditorUtils::FindBlueprintForGraph(TheHoveredGraph)) && VariableProperty->GetOwner<UFunction>())
{
SetFeedbackMessageError(FText::Format( LOCTEXT("IncorrectGraphForLocalVariable_Error", "Cannot place local variable '{VariableName}' in external scope '{Scope}'"), Args));
}
else
{
SetFeedbackMessageError(FText::Format( LOCTEXT("IncorrectGraphForVariable_Error", "Cannot place variable '{VariableName}' in external scope '{Scope}'"), Args));
}
}
else if (PinUnderCursor)
{
FFormatNamedArguments Args;
Args.Add(TEXT("PinUnderCursor"), FText::FromName(PinUnderCursor->PinName));
Args.Add(TEXT("VariableName"), FText::FromString(VariableString));
if (CanVariableBeDropped(VariableProperty, *PinUnderCursor->GetOwningNode()->GetGraph()))
{
if (PinUnderCursor->bOrphanedPin)
{
SetFeedbackMessageError(FText::Format(LOCTEXT("OrphanedPin_Error", "Cannot make connection to orphaned pin {PinUnderCursor}"), Args));
}
else if (const UEdGraphSchema_K2* Schema = Cast<const UEdGraphSchema_K2>(PinUnderCursor->GetSchema()))
{
const bool bIsExecPin = Schema->IsExecPin(*PinUnderCursor);
const bool bIsRead = (PinUnderCursor->Direction == EGPD_Input) && !bIsExecPin;
const UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNode(PinUnderCursor->GetOwningNode());
const bool bWritableProperty = (FBlueprintEditorUtils::IsPropertyWritableInBlueprint(Blueprint, VariableProperty) == FBlueprintEditorUtils::EPropertyWritableState::Writable);
const bool bCanWriteIfNeeded = bIsRead || bWritableProperty;
FEdGraphPinType VariablePinType;
Schema->ConvertPropertyToPinType(VariableProperty, VariablePinType);
const bool bTypeMatch = Schema->ArePinTypesCompatible(VariablePinType, PinUnderCursor->PinType) || bIsExecPin;
const bool bCanAutoConvert = Schema->FindSpecializedConversionNode(VariablePinType, *PinUnderCursor, false).IsSet();
bool bCanAutocast = false;
if (PinUnderCursor->Direction == EGPD_Output)
{
bCanAutocast = Schema->SearchForAutocastFunction(PinUnderCursor->PinType, VariablePinType).IsSet();
}
else
{
bCanAutocast = Schema->SearchForAutocastFunction(VariablePinType, PinUnderCursor->PinType).IsSet();
}
Args.Add(TEXT("PinUnderCursor"), FText::FromName(PinUnderCursor->PinName));
if ((bTypeMatch || bCanAutocast || bCanAutoConvert) && bCanWriteIfNeeded)
{
SetFeedbackMessageOK(bIsRead ?
FText::Format(LOCTEXT("MakeThisEqualThat_PinEqualVariableName", "Make {PinUnderCursor} = {VariableName}"), Args) :
FText::Format(LOCTEXT("MakeThisEqualThat_VariableNameEqualPin", "Make {VariableName} = {PinUnderCursor}"), Args));
}
else
{
SetFeedbackMessageError(bCanWriteIfNeeded ?
FText::Format(LOCTEXT("NotCompatible_Error", "The type of '{VariableName}' is not compatible with {PinUnderCursor}"), Args) :
FText::Format(LOCTEXT("ReadOnlyVar_Error", "Cannot write to read-only variable '{VariableName}'"), Args));
}
}
}
else
{
Args.Add(TEXT("Scope"), FText::FromString(PinUnderCursor->GetOwningNode()->GetGraph()->GetName()));
SetFeedbackMessageError(FText::Format( LOCTEXT("IncorrectGraphForPin_Error", "Cannot place local variable '{VariableName}' in external scope '{Scope}'"), Args));
}
}
else if (VarNodeUnderCursor)
{
FFormatNamedArguments Args;
Args.Add(TEXT("VariableName"), FText::FromString(VariableString));
if (CanVariableBeDropped(VariableProperty, *VarNodeUnderCursor->GetGraph()))
{
const bool bIsRead = VarNodeUnderCursor->IsA(UK2Node_VariableGet::StaticClass());
const UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNode(VarNodeUnderCursor);
const bool bWritableProperty = (FBlueprintEditorUtils::IsPropertyWritableInBlueprint(Blueprint, VariableProperty) == FBlueprintEditorUtils::EPropertyWritableState::Writable);
const bool bCanWriteIfNeeded = bIsRead || bWritableProperty;
// If this node has split pins then the reconstruction cannot handle it, so don't allow it
const bool bHasSplitPins = NodeHasSplitPins(VarNodeUnderCursor);
if (bHasSplitPins)
{
SetFeedbackMessageError(FText::Format(LOCTEXT("SplitPinVar_Error", "Cannot change '{VariableName}' because it has split pins"), Args));
}
else if (bCanWriteIfNeeded)
{
Args.Add(TEXT("ReadOrWrite"), bIsRead ? LOCTEXT("Read", "read") : LOCTEXT("Write", "write"));
SetFeedbackMessageOK(WillBreakLinks(VarNodeUnderCursor, VariableProperty) ?
FText::Format(LOCTEXT("ChangeNodeToWarnBreakLinks", "Change node to {ReadOrWrite} '{VariableName}', WARNING this will break links!"), Args) :
FText::Format(LOCTEXT("ChangeNodeTo", "Change node to {ReadOrWrite} '{VariableName}'"), Args));
}
else
{
SetFeedbackMessageError(FText::Format( LOCTEXT("ReadOnlyVar_Error", "Cannot write to read-only variable '{VariableName}'"), Args));
}
}
else
{
Args.Add(TEXT("Scope"), FText::FromString(VarNodeUnderCursor->GetGraph()->GetName()));
SetFeedbackMessageError(FText::Format( LOCTEXT("IncorrectGraphForNodeReplace_Error", "Cannot replace node with local variable '{VariableName}' in external scope '{Scope}'"), Args));
}
}
else if (bAltDrag && !bCanMakeSetter)
{
FFormatNamedArguments Args;
Args.Add(TEXT("VariableName"), FText::FromString(VariableString));
SetFeedbackMessageError(FText::Format(LOCTEXT("CannotPlaceSetter", "Variable '{VariableName}' is readonly, you cannot set this variable."), Args));
}
else
{
FMyBlueprintItemDragDropAction::HoverTargetChanged();
}
}
void FKismetVariableDragDropAction::GetDefaultStatusSymbol(const FSlateBrush*& PrimaryBrushOut, FSlateColor& IconColorOut, FSlateBrush const*& SecondaryBrushOut, FSlateColor& SecondaryColorOut) const
{
PrimaryBrushOut = FBlueprintEditor::GetVarIconAndColor(VariableSource.Get(), VariableName, IconColorOut, SecondaryBrushOut, SecondaryColorOut);
}
FReply FKismetVariableDragDropAction::DroppedOnPin(const FVector2f& ScreenPosition, const FVector2f& GraphPosition)
{
if (UEdGraphPin* TargetPin = GetHoveredPin())
{
if (!TargetPin->bOrphanedPin)
{
FProperty* VariableProperty = GetVariableProperty();
if (!TargetPin->GetSchema()->CanVariableBeDropped(TargetPin->GetOwningNode()->GetGraph(), VariableProperty))
{
return FReply::Unhandled();
}
bool DropReply = ((UEdGraphSchema*)TargetPin->GetSchema())->RequestVariableDropOnPin(TargetPin->GetOwningNode()->GetGraph(), VariableProperty, TargetPin, FDeprecateSlateVector2D(GraphPosition), FDeprecateSlateVector2D(ScreenPosition));
if (DropReply)
{
return FReply::Handled();
}
if (const UEdGraphSchema_K2* Schema = Cast<const UEdGraphSchema_K2>(TargetPin->GetSchema()))
{
const bool bIsExecPin = Schema->IsExecPin(*TargetPin);
if (CanVariableBeDropped(VariableProperty, *TargetPin->GetOwningNode()->GetGraph()))
{
const bool bIsRead = (TargetPin->Direction == EGPD_Input) && !bIsExecPin;
const UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNode(TargetPin->GetOwningNode());
const bool bWritableProperty = (FBlueprintEditorUtils::IsPropertyWritableInBlueprint(Blueprint, VariableProperty) == FBlueprintEditorUtils::EPropertyWritableState::Writable);
const bool bCanWriteIfNeeded = bIsRead || bWritableProperty;
FEdGraphPinType VariablePinType;
Schema->ConvertPropertyToPinType(VariableProperty, VariablePinType);
const bool bTypeMatch = Schema->ArePinTypesCompatible(VariablePinType, TargetPin->PinType) || bIsExecPin;
const bool bCanAutoConvert = Schema->FindSpecializedConversionNode(VariablePinType, *TargetPin, false).IsSet();
bool bCanAutocast = false;
if (TargetPin->Direction == EGPD_Output)
{
bCanAutocast = Schema->SearchForAutocastFunction(TargetPin->PinType, VariablePinType).IsSet();
}
else
{
bCanAutocast = Schema->SearchForAutocastFunction(VariablePinType, TargetPin->PinType).IsSet();
}
if ((bTypeMatch || bCanAutocast || bCanAutoConvert) && bCanWriteIfNeeded)
{
FEdGraphSchemaAction_K2NewNode Action;
UK2Node_Variable* VarNode = bIsRead ? (UK2Node_Variable*)NewObject<UK2Node_VariableGet>() : (UK2Node_Variable*)NewObject<UK2Node_VariableSet>();
Action.NodeTemplate = VarNode;
UBlueprint* DropOnBlueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TargetPin->GetOwningNode()->GetGraph());
UEdGraphSchema_K2::ConfigureVarNode(VarNode, VariableName, VariableSource.Get(), DropOnBlueprint);
Action.PerformAction(TargetPin->GetOwningNode()->GetGraph(), TargetPin, FDeprecateSlateVector2D(GraphPosition));
}
}
}
}
}
return FReply::Handled();
}
FReply FKismetVariableDragDropAction::DroppedOnNode(const FVector2f& ScreenPosition, const FVector2f& GraphPosition)
{
if (UEdGraphNode* TargetNode = GetHoveredNode())
{
FProperty* VariableProperty = GetVariableProperty();
if (!TargetNode->GetSchema()->CanVariableBeDropped(TargetNode->GetGraph(), VariableProperty))
{
return FReply::Unhandled();
}
bool DropReply = ((UEdGraphSchema*)TargetNode->GetSchema())->RequestVariableDropOnNode(TargetNode->GetGraph(), VariableProperty, TargetNode, FDeprecateSlateVector2D(GraphPosition), FDeprecateSlateVector2D(ScreenPosition));
if (DropReply)
{
return FReply::Handled();
}
}
UK2Node_Variable* TargetNode = Cast<UK2Node_Variable>(GetHoveredNode());
if (TargetNode && (VariableName != TargetNode->GetVarName()))
{
FProperty* VariableProperty = GetVariableProperty();
if (CanVariableBeDropped(VariableProperty, *TargetNode->GetGraph()) && !NodeHasSplitPins(TargetNode))
{
const FScopedTransaction Transaction(LOCTEXT("ReplacePinVariable", "Replace Pin Variable"));
const FName OldVarName = TargetNode->GetVarName();
const UEdGraphSchema_K2* Schema = Cast<const UEdGraphSchema_K2>(TargetNode->GetSchema());
TArray<class UEdGraphPin*> BadLinks;
GetLinksThatWillBreak(TargetNode,VariableProperty,BadLinks);
// Change the variable name and context
UBlueprint* DropOnBlueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TargetNode->GetGraph());
UEdGraphPin* Pin = TargetNode->FindPin(OldVarName);
DropOnBlueprint->Modify();
TargetNode->Modify();
if (Pin != nullptr)
{
Pin->Modify();
}
UEdGraphSchema_K2::ConfigureVarNode(TargetNode, VariableName, VariableSource.Get(), DropOnBlueprint);
if ((Pin == nullptr) || (Pin->LinkedTo.Num() == BadLinks.Num()) || (Schema == nullptr))
{
TargetNode->GetSchema()->ReconstructNode(*TargetNode);
}
else
{
FEdGraphPinType NewPinType;
Schema->ConvertPropertyToPinType(VariableProperty,NewPinType);
Pin->PinName = VariableName;
Pin->PinType = NewPinType;
// break bad links
for (TArray<class UEdGraphPin*>::TIterator OtherPinIt(BadLinks);OtherPinIt;++OtherPinIt)
{
Pin->BreakLinkTo(*OtherPinIt);
}
}
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(DropOnBlueprint);
return FReply::Handled();
}
}
return FReply::Unhandled();
}
void FKismetVariableDragDropAction::MakeGetter(FNodeConstructionParams InParams)
{
check(InParams.Graph);
const UEdGraphSchema_K2* K2_Schema = Cast<const UEdGraphSchema_K2>(InParams.Graph->GetSchema());
if (K2_Schema)
{
K2_Schema->SpawnVariableGetNode(InParams.GraphPosition, InParams.Graph, InParams.VariableName, InParams.VariableSource.Get());
}
}
void FKismetVariableDragDropAction::MakeSetter(FNodeConstructionParams InParams)
{
check(InParams.Graph);
const UEdGraphSchema_K2* K2_Schema = Cast<const UEdGraphSchema_K2>(InParams.Graph->GetSchema());
if (K2_Schema)
{
K2_Schema->SpawnVariableSetNode(InParams.GraphPosition, InParams.Graph, InParams.VariableName, InParams.VariableSource.Get());
}
}
bool FKismetVariableDragDropAction::CanExecuteMakeSetter(FNodeConstructionParams InParams, FProperty* InVariableProperty)
{
check(InVariableProperty);
check(InParams.VariableSource.Get());
check(InParams.Graph);
if(UClass* VariableSourceClass = Cast<UClass>(InParams.VariableSource.Get()))
{
const UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(InParams.Graph);
const bool bWritableProperty = (FBlueprintEditorUtils::IsPropertyWritableInBlueprint(Blueprint, InVariableProperty) == FBlueprintEditorUtils::EPropertyWritableState::Writable);
bool bSupportsImpureNodes = false;
const UEdGraphSchema_K2* K2_Schema = Cast<const UEdGraphSchema_K2>(InParams.Graph->GetSchema());
if (K2_Schema)
{
bSupportsImpureNodes = K2_Schema->DoesGraphSupportImpureFunctions(InParams.Graph);
}
return (bSupportsImpureNodes && bWritableProperty && !VariableSourceClass->HasAnyClassFlags(CLASS_Const));
}
return true;
}
FReply FKismetVariableDragDropAction::DroppedOnPanel( const TSharedRef< SWidget >& Panel, const FVector2f& ScreenPosition, const FVector2f& GraphPosition, UEdGraph& Graph)
{
FProperty* VariableProperty = GetVariableProperty();
bool DropReply = ((UEdGraphSchema*)Graph.GetSchema())->RequestVariableDropOnPanel(GetHoveredGraph(), VariableProperty, FDeprecateSlateVector2D(GraphPosition), FDeprecateSlateVector2D(ScreenPosition));
if (DropReply)
{
return FReply::Handled();
}
if (Graph.GetSchema()->IsA<UEdGraphSchema_K2>())
{
if (VariableProperty && CanVariableBeDropped(VariableProperty, Graph))
{
UStruct* Outer = VariableProperty->GetOwnerChecked<UStruct>();
FNodeConstructionParams NewNodeParams;
NewNodeParams.VariableName = VariableName;
NewNodeParams.Graph = &Graph;
NewNodeParams.GraphPosition = GraphPosition;
NewNodeParams.VariableSource= Outer;
// call analytics
AnalyticCallback.ExecuteIfBound();
// Take into account current state of modifier keys in case the user changed their mind
FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys();
const bool bModifiedKeysActive = ModifierKeys.IsControlDown() || ModifierKeys.IsAltDown();
const bool bAutoCreateGetter = bModifiedKeysActive ? ModifierKeys.IsControlDown() : bControlDrag;
const bool bAutoCreateSetter = bModifiedKeysActive ? ModifierKeys.IsAltDown() : bAltDrag;
// Handle Getter/Setters
if (bAutoCreateGetter || bAutoCreateSetter)
{
if (bAutoCreateGetter || !CanExecuteMakeSetter(NewNodeParams, VariableProperty))
{
MakeGetter(NewNodeParams);
NewNodeParams.GraphPosition.Y += 50.f;
}
if (bAutoCreateSetter && CanExecuteMakeSetter( NewNodeParams, VariableProperty))
{
MakeSetter(NewNodeParams);
}
}
// Show selection menu
else
{
FMenuBuilder MenuBuilder(true, NULL);
const FText VariableNameText = FText::FromName( VariableName );
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::CreateStatic(&FKismetVariableDragDropAction::MakeGetter, NewNodeParams), 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::CreateStatic(&FKismetVariableDragDropAction::MakeSetter, NewNodeParams),
FCanExecuteAction::CreateStatic(&FKismetVariableDragDropAction::CanExecuteMakeSetter, NewNodeParams, VariableProperty ))
);
TSharedRef< SWidget > PanelWidget = Panel;
// Show dialog to choose getter vs setter
FSlateApplication::Get().PushMenu(
PanelWidget,
FWidgetPath(),
MenuBuilder.MakeWidget(),
ScreenPosition,
FPopupTransitionEffect( FPopupTransitionEffect::ContextMenu)
);
MenuBuilder.EndSection();
}
}
}
return FReply::Handled();
}
bool FKismetVariableDragDropAction::CanVariableBeDropped(const FProperty* InVariableProperty, const UEdGraph& InGraph) const
{
bool bCanVariableBeDropped = false;
if (InVariableProperty)
{
// Only allow variables to be placed within the same blueprint (otherwise the self context on the dropped node will be invalid)
bCanVariableBeDropped = IsFromBlueprint(FBlueprintEditorUtils::FindBlueprintForGraph(&InGraph));
// Local variables have some special conditions for being allowed to be placed
if (bCanVariableBeDropped && InVariableProperty->GetOwner<UFunction>())
{
// Check if the top level graph has the same name as the function, if they do not then the variable cannot be placed in the graph
if (FBlueprintEditorUtils::GetTopLevelGraph(&InGraph)->GetFName() != InVariableProperty->GetOwner<UFunction>()->GetFName())
{
bCanVariableBeDropped = false;
}
}
}
return bCanVariableBeDropped;
}
UStruct* FKismetVariableDragDropAction::GetLocalVariableScope() const
{
if( VariableSource->IsA(UFunction::StaticClass()) )
{
return VariableSource.Get();
}
return NULL;
}
#undef LOCTEXT_NAMESPACE