#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(); // Setup the node title TSharedPtr 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 NewPin = SNew(SGraphPin, Pin); AddPin(NewPin.ToSharedRef()); } } } void SDismembermentGraphNode::AddPin(const TSharedRef& PinToAdd) { PinToAdd->SetOwner(SharedThis(this)); const UEdGraphPin* PinObj = PinToAdd->GetPinObj(); const bool bAdvancedParameter = PinObj && PinObj->bAdvancedView; if (bAdvancedParameter) { PinToAdd->SetVisibility(TAttribute(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 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(GraphNode); } TSharedRef 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 SDismembermentGraphNode::GetNodeBodyWidget() { UDismembermentGraphNode* DismembermentNode = GetDismembermentGraphNode(); if (!DismembermentNode) { return SNew(STextBlock).Text(NSLOCTEXT("SDismembermentGraphNode", "InvalidNode", "Invalid Node")); } return SNew(STextBlock) .Text(DismembermentNode->GetTooltipText()) .TextStyle(FEditorStyle::Get(), "Graph.StateNode.Description") .WrapTextAt(200.0f); } TSharedRef 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(FEditorStyle::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