Files
FLESH/Source/FLESHEditor/Private/DismembermentGraph/SDismembermentGraphNode.cpp
2025-04-18 18:17:02 +08:00

379 lines
12 KiB
C++

#include "DismembermentGraph/SDismembermentGraphNode.h"
#include "DismembermentGraph/DismembermentGraphNode.h"
#include "GraphEditorSettings.h"
#include "SGraphPin.h"
#include "SlateOptMacros.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Text/SInlineEditableTextBlock.h"
#include "Widgets/Images/SImage.h"
#include "Styling/AppStyle.h"
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SDismembermentGraphNode::Construct(const FArguments& InArgs, UEdGraphNode* InNode)
{
GraphNode = InNode;
UpdateGraphNode();
bIsHovered = false;
}
void SDismembermentGraphNode::UpdateGraphNode()
{
// Clear the widget
ContentScale.Bind(this, &SGraphNode::GetContentScale);
LeftNodeBox.Reset();
RightNodeBox.Reset();
// OutputPinBox.Reset(); // Comment out this line because OutputPinBox is not defined
// Setup the node title
TSharedPtr<SNodeTitle> NodeTitle = SNew(SNodeTitle, GraphNode);
// Create the node body
this->ContentScale.Bind(this, &SGraphNode::GetContentScale);
this->GetOrAddSlot(ENodeZone::Center)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("Graph.StateNode.Body"))
.BorderBackgroundColor(this, &SDismembermentGraphNode::GetNodeColor)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(0.0f)
[
SNew(SOverlay)
// Main node body
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
[
SNew(SVerticalBox)
// Title bar
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("Graph.StateNode.ColorSpill"))
.BorderBackgroundColor(this, &SDismembermentGraphNode::GetNodeTitleColor)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Center)
.Padding(FMargin(10.0f, 5.0f))
[
SNew(SHorizontalBox)
// Node title
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(STextBlock)
.Text(this, &SDismembermentGraphNode::GetNodeTitle)
.TextStyle(FAppStyle::Get(), "Graph.StateNode.NodeTitle")
.Margin(FMargin(0.0f, 0.0f, 4.0f, 0.0f))
]
// Node category
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(STextBlock)
.Text(this, &SDismembermentGraphNode::GetNodeCategory)
.TextStyle(FAppStyle::Get(), "Graph.StateNode.NodeTitle")
.ColorAndOpacity(FLinearColor(0.8f, 0.8f, 0.8f))
.Margin(FMargin(4.0f, 0.0f, 0.0f, 0.0f))
]
]
]
// Node content
+ SVerticalBox::Slot()
.Padding(0.0f)
.AutoHeight()
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("NoBorder"))
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(FMargin(10.0f, 0.0f))
[
SNew(SVerticalBox)
// Node description
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(FMargin(0.0f, 4.0f))
[
SNew(STextBlock)
.Text(this, &SDismembermentGraphNode::GetNodeDescription)
.TextStyle(FAppStyle::Get(), "Graph.StateNode.Description")
.WrapTextAt(200.0f)
]
// Node preview
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(FMargin(0.0f, 4.0f))
[
GetNodePreviewWidget()
]
]
]
// Input pins
+ SVerticalBox::Slot()
.AutoHeight()
[
SAssignNew(LeftNodeBox, SVerticalBox)
]
// Output pins
+ SVerticalBox::Slot()
.AutoHeight()
[
SAssignNew(RightNodeBox, SVerticalBox)
]
]
]
];
// Create all the pins
CreatePinWidgets();
}
void SDismembermentGraphNode::CreatePinWidgets()
{
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
if (!DismembermentNode)
{
return;
}
// Create the input pins
for (int32 PinIndex = 0; PinIndex < DismembermentNode->Pins.Num(); PinIndex++)
{
UEdGraphPin* Pin = DismembermentNode->Pins[PinIndex];
if (!Pin->bHidden)
{
TSharedPtr<SGraphPin> NewPin = SNew(SGraphPin, Pin);
AddPin(NewPin.ToSharedRef());
}
}
}
void SDismembermentGraphNode::AddPin(const TSharedRef<SGraphPin>& PinToAdd)
{
PinToAdd->SetOwner(SharedThis(this));
const UEdGraphPin* PinObj = PinToAdd->GetPinObj();
const bool bAdvancedParameter = PinObj && PinObj->bAdvancedView;
if (bAdvancedParameter)
{
PinToAdd->SetVisibility(TAttribute<EVisibility>(PinToAdd, &SGraphPin::IsPinVisibleAsAdvanced));
}
if (PinToAdd->GetDirection() == EGPD_Input)
{
LeftNodeBox->AddSlot()
.AutoHeight()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
.Padding(10.0f, 4.0f)
[
PinToAdd
];
InputPins.Add(PinToAdd);
}
else // EGPD_Output
{
RightNodeBox->AddSlot()
.AutoHeight()
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
.Padding(10.0f, 4.0f)
[
PinToAdd
];
OutputPins.Add(PinToAdd);
}
}
TSharedPtr<SToolTip> SDismembermentGraphNode::GetComplexTooltip()
{
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
if (!DismembermentNode)
{
return nullptr;
}
return SNew(SToolTip)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(FMargin(0.0f, 2.0f))
[
SNew(STextBlock)
.Text(DismembermentNode->GetNodeTitle(ENodeTitleType::FullTitle))
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 12))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(FMargin(0.0f, 2.0f))
[
SNew(STextBlock)
.Text(DismembermentNode->GetTooltipText())
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 10))
]
];
}
FReply SDismembermentGraphNode::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
return FReply::Handled().DetectDrag(SharedThis(this), EKeys::LeftMouseButton);
}
FReply SDismembermentGraphNode::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
return FReply::Handled();
}
FReply SDismembermentGraphNode::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
return FReply::Unhandled();
}
void SDismembermentGraphNode::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
bIsHovered = true;
SGraphNode::OnMouseEnter(MyGeometry, MouseEvent);
}
void SDismembermentGraphNode::OnMouseLeave(const FPointerEvent& MouseEvent)
{
bIsHovered = false;
SGraphNode::OnMouseLeave(MouseEvent);
}
UDismembermentGraphNode* SDismembermentGraphNode::GetDismembermentGraphNode() const
{
return Cast<UDismembermentGraphNode>(GraphNode);
}
TSharedRef<SWidget> SDismembermentGraphNode::GetNodeTitleWidget()
{
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
if (!DismembermentNode)
{
return SNew(STextBlock).Text(NSLOCTEXT("SDismembermentGraphNode", "InvalidNode", "Invalid Node"));
}
return SNew(STextBlock)
.Text(DismembermentNode->GetNodeTitle(ENodeTitleType::FullTitle))
.TextStyle(FAppStyle::Get(), "Graph.StateNode.NodeTitle");
}
TSharedRef<SWidget> SDismembermentGraphNode::GetNodeBodyWidget()
{
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
if (!DismembermentNode)
{
return SNew(STextBlock).Text(NSLOCTEXT("SDismembermentGraphNode", "InvalidNode", "Invalid Node"));
}
return SNew(STextBlock)
.Text(DismembermentNode->GetTooltipText())
.TextStyle(FAppStyle::Get(), "Graph.StateNode.Description")
.WrapTextAt(200.0f);
}
TSharedRef<SWidget> SDismembermentGraphNode::GetNodePreviewWidget()
{
// This can be customized for different node types to show a preview
return SNew(SBox)
.WidthOverride(100.0f)
.HeightOverride(100.0f)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("Graph.StateNode.Body"))
.BorderBackgroundColor(FLinearColor(0.1f, 0.1f, 0.1f, 0.5f))
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(NSLOCTEXT("SDismembermentGraphNode", "Preview", "Preview"))
.TextStyle(FAppStyle::Get(), "Graph.StateNode.Description")
]
];
}
FSlateColor SDismembermentGraphNode::GetNodeColor() const
{
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
if (!DismembermentNode)
{
return FLinearColor::Black;
}
FLinearColor NodeColor = DismembermentNode->NodeTitleColor;
if (IsNodeSelected())
{
NodeColor = FLinearColor(1.0f, 0.5f, 0.0f);
}
else if (IsNodeHovered())
{
NodeColor = NodeColor * 1.2f;
}
return NodeColor;
}
FSlateColor SDismembermentGraphNode::GetNodeTitleColor() const
{
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
if (!DismembermentNode)
{
return FLinearColor::Black;
}
return DismembermentNode->NodeTitleColor.LinearRGBToHSV();
}
FText SDismembermentGraphNode::GetNodeTitle() const
{
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
if (!DismembermentNode)
{
return NSLOCTEXT("SDismembermentGraphNode", "InvalidNode", "Invalid Node");
}
return DismembermentNode->GetNodeTitle(ENodeTitleType::FullTitle);
}
FText SDismembermentGraphNode::GetNodeCategory() const
{
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
if (!DismembermentNode)
{
return FText::GetEmpty();
}
return DismembermentNode->NodeCategory;
}
FText SDismembermentGraphNode::GetNodeDescription() const
{
UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode();
if (!DismembermentNode)
{
return FText::GetEmpty();
}
return DismembermentNode->NodeDescription;
}
bool SDismembermentGraphNode::IsNodeSelected() const
{
return GraphNode && GraphNode->IsSelected();
}
bool SDismembermentGraphNode::IsNodeHovered() const
{
return bIsHovered;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION