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

296 lines
7.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SKismetLinearExpression.h"
#include "Containers/Array.h"
#include "Containers/EnumAsByte.h"
#include "EdGraph/EdGraphNode.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraph/EdGraphSchema.h"
#include "HAL/PlatformCrt.h"
#include "HAL/PlatformMath.h"
#include "Internationalization/Internationalization.h"
#include "Internationalization/Text.h"
#include "K2Node.h"
#include "K2Node_VariableGet.h"
#include "Layout/Children.h"
#include "Layout/Margin.h"
#include "Layout/Visibility.h"
#include "SlotBase.h"
#include "Styling/AppStyle.h"
#include "Styling/SlateColor.h"
#include "Templates/Casts.h"
#include "Types/SlateEnums.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Input/SEditableText.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/SOverlay.h"
#include "Widgets/Text/STextBlock.h"
class SWidget;
#define LOCTEXT_NAMESPACE "KismetLinearExpression"
//////////////////////////////////////////////////////////////////////////
// SKismetLinearExpression
void SKismetLinearExpression::Construct(const FArguments& InArgs, UEdGraphPin* InitialInputPin)
{
SetExpressionRoot(InitialInputPin);
}
void SKismetLinearExpression::SetExpressionRoot(UEdGraphPin* InputPin)
{
this->ChildSlot
[
MakePinWidget(InputPin)
];
VisitedNodes.Empty();
}
TSharedRef<SWidget> SKismetLinearExpression::MakeNodeWidget(const UEdGraphNode* Node, const UEdGraphPin* FromPin)
{
// Make sure we visit each node only once; to prevent infinite recursion
bool bAlreadyInSet = false;
VisitedNodes.Add(Node, &bAlreadyInSet);
if (bAlreadyInSet)
{
return SNew(STextBlock).Text(LOCTEXT("RecursionOccurredInNodeGraphMessage", "RECURSION"));
}
// Evaluate the node to gather information that many of the widgets will want
const UEdGraphSchema* Schema = Node->GetSchema();
TArray<UEdGraphPin*> InputPins;
TArray<UEdGraphPin*> OutputPins;
int32 InputPinCount = 0;
int32 OutputPinCount = 0;
for (auto PinIt = Node->Pins.CreateConstIterator(); PinIt; ++PinIt)
{
UEdGraphPin* Pin = *PinIt;
if (!Pin->bHidden)
{
if (Pin->Direction == EGPD_Input)
{
InputPins.Add(Pin);
++InputPinCount;
}
else
{
OutputPins.Add(Pin);
++OutputPinCount;
}
}
}
// Determine if the node is impure
bool bIsPure = false;
if (const UK2Node* K2Node = Cast<const UK2Node>(Node))
{
bIsPure = K2Node->IsNodePure();
}
// If the node is
if ((OutputPinCount != 1) || !bIsPure)
{
// The source node is impure or has multiple outputs, so cannot be directly part of this pure expression
// Instead show it as a special sort of variable get
FFormatNamedArguments Args;
Args.Add(TEXT("NodeTitle"), Node->GetNodeTitle(ENodeTitleType::ListView));
Args.Add(TEXT("PinName"), FromPin->GetDisplayName());
const FText EffectiveVariableName = FText::Format(LOCTEXT("NodeTitleWithPinName", "{NodeTitle}_{PinName}"), Args );
return SNew(SOverlay)
+ SOverlay::Slot()
[
SNew(SImage)
.Image( FAppStyle::GetBrush("KismetExpression.ReadAutogeneratedVariable.Body") )
.ColorAndOpacity( Schema->GetPinTypeColor(FromPin->PinType) )
]
+ SOverlay::Slot()
.Padding( FMargin(6,4) )
[
SNew(STextBlock)
.TextStyle( FAppStyle::Get(), TEXT("KismetExpression.ReadAutogeneratedVariable") )
.Text( EffectiveVariableName )
];
}
else if (auto VarGetNode = Cast<const UK2Node_VariableGet>(Node))
{
// Variable get node
return SNew(SOverlay)
+ SOverlay::Slot()
[
SNew(SImage)
.Image( FAppStyle::GetBrush("KismetExpression.ReadVariable.Body") )
.ColorAndOpacity( Schema->GetPinTypeColor(OutputPins[0]->PinType) )
]
+ SOverlay::Slot()
.Padding( FMargin(6,4) )
[
//SNew(STextBlock)
SNew(SEditableText)
//.TextStyle( FAppStyle::Get(), TEXT("KismetExpression.ReadVariable") )
.Text( FText::FromString( VarGetNode->GetVarNameString() ) )
]
+ SOverlay::Slot()
[
SNew(SImage)
.Visibility(EVisibility::HitTestInvisible)
.Image( FAppStyle::GetBrush("KismetExpression.ReadVariable.Gloss") )
];
/*
+ SOverlay::Slot()
.Padding( FMargin(6,4) )
[
SNew(STextBlock)
.TextStyle( FAppStyle::Get(), TEXT("KismetExpression.ReadVariable") )
.Text( VarGetNode->VariableName.ToString() )
];
*/
}
else if (const UK2Node* AnyNode = Cast<const UK2Node>(Node))
{
const bool bIsCompact = AnyNode->ShouldDrawCompact() && (InputPinCount <= 2);
TSharedRef<SWidget> OperationWidget =
SNew(STextBlock)
.TextStyle( FAppStyle::Get(), bIsCompact ? TEXT("KismetExpression.OperatorNode") : TEXT("KismetExpression.FunctionNode") )
.Text(AnyNode->GetCompactNodeTitle());
if ((InputPinCount == 1) && bIsCompact)
{
// One-pin compact nodes are assumed to be unary operators
return SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(4, 0)
[
OperationWidget
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
MakePinWidget(InputPins[0])
];
}
else if ((InputPinCount == 2) && bIsCompact)
{
// Two-pin compact nodes are assumed to be binary operators
return SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
MakePinWidget(InputPins[0])
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(4, 0)
[
OperationWidget
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
MakePinWidget(InputPins[1])
];
}
else
{
// All other operations are treated as traditional function calls
// Create the argument list
TSharedRef<SHorizontalBox> InnerBox =
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(4.0f)
[
SNew(STextBlock).Text(LOCTEXT("BeginExpression", "("))
];
for (auto PinIt = InputPins.CreateConstIterator(); PinIt; )
{
InnerBox->AddSlot()
.AutoWidth()
.VAlign(VAlign_Center)
[
MakePinWidget(*PinIt)
];
++PinIt;
if (PinIt)
{
InnerBox->AddSlot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(4)
[
SNew(STextBlock).Text(LOCTEXT("NextExpression", ", "))
];
}
}
InnerBox->AddSlot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(4)
[
SNew(STextBlock).Text(LOCTEXT("EndExpression", ")"))
];
// Combine the function name and argument list
return SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
OperationWidget
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
InnerBox
];
}
}
else
{
return SNew(STextBlock).Text(LOCTEXT("UnknownNodeMessage", "UNKNOWN_NODE"));
}
}
TSharedRef<SWidget> SKismetLinearExpression::MakePinWidget(const UEdGraphPin* Pin)
{
if (Pin == NULL)
{
return SNew(STextBlock)
.Text( LOCTEXT("BanGraphPinMessage", "BAD PIN") );
}
else if (Pin->LinkedTo.Num() == 0)
{
// Input pins with no links are displayed as their literals
return SNew(STextBlock)
.TextStyle( FAppStyle::Get(), TEXT("KismetExpression.LiteralValue") )
.Text(FText::FromString(Pin->GetDefaultAsString()));
}
else
{
// Evaluate the node they're connected to
UEdGraphPin* SourcePin = Pin->LinkedTo[0];
return MakeNodeWidget(SourcePin->GetOwningNode(), SourcePin);
}
}
#undef LOCTEXT_NAMESPACE