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

369 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SGraphNodeAI.h"
#include "AIGraphNode.h"
#include "Framework/Application/SlateApplication.h"
#include "Editor.h"
#include "AIGraph.h"
#include "SGraphPanel.h"
#include "ScopedTransaction.h"
#define LOCTEXT_NAMESPACE "AIGraph"
TSharedRef<FDragAIGraphNode> FDragAIGraphNode::New(const TSharedRef<SGraphPanel>& InGraphPanel, const TSharedRef<SGraphNode>& InDraggedNode)
{
TSharedRef<FDragAIGraphNode> Operation = MakeShareable(new FDragAIGraphNode);
Operation->StartTime = FPlatformTime::Seconds();
Operation->GraphPanel = InGraphPanel;
Operation->DraggedNodes.Add(InDraggedNode);
// adjust the decorator away from the current mouse location a small amount based on cursor size
Operation->DecoratorAdjust = FSlateApplication::Get().GetCursorSize();
Operation->Construct();
return Operation;
}
TSharedRef<FDragAIGraphNode> FDragAIGraphNode::New(const TSharedRef<SGraphPanel>& InGraphPanel, const TArray< TSharedRef<SGraphNode> >& InDraggedNodes)
{
TSharedRef<FDragAIGraphNode> Operation = MakeShareable(new FDragAIGraphNode);
Operation->StartTime = FPlatformTime::Seconds();
Operation->GraphPanel = InGraphPanel;
Operation->DraggedNodes = InDraggedNodes;
Operation->DecoratorAdjust = FSlateApplication::Get().GetCursorSize();
Operation->Construct();
return Operation;
}
UAIGraphNode* FDragAIGraphNode::GetDropTargetNode() const
{
return Cast<UAIGraphNode>(GetHoveredNode());
}
void SGraphPinAI::Construct(const FArguments& InArgs, UEdGraphPin* InPin)
{
this->SetCursor(EMouseCursor::Default);
bShowLabel = true;
GraphPinObj = InPin;
check(GraphPinObj != NULL);
const UEdGraphSchema* Schema = GraphPinObj->GetSchema();
check(Schema);
SBorder::Construct(SBorder::FArguments()
.BorderImage(this, &SGraphPinAI::GetPinBorder)
.BorderBackgroundColor(this, &SGraphPinAI::GetPinColor)
.OnMouseButtonDown(this, &SGraphPinAI::OnPinMouseDown)
.Cursor(this, &SGraphPinAI::GetPinCursor)
.Padding(FMargin(10.0f))
);
}
TSharedRef<SWidget> SGraphPinAI::GetDefaultValueWidget()
{
return SNew(STextBlock);
}
const FSlateBrush* SGraphPinAI::GetPinBorder() const
{
return FAppStyle::GetBrush(TEXT("Graph.StateNode.Body"));
}
FSlateColor SGraphPinAI::GetPinColor() const
{
return FSlateColor(IsHovered() ? FLinearColor::Yellow : FLinearColor::Black);
}
void SGraphNodeAI::Construct(const FArguments& InArgs, UAIGraphNode* InNode)
{
SetCursor(EMouseCursor::CardinalCross);
GraphNode = InNode;
UpdateGraphNode();
bDragMarkerVisible = false;
}
void SGraphNodeAI::AddSubNode(TSharedPtr<SGraphNode> SubNodeWidget)
{
SubNodes.Add(SubNodeWidget);
}
FText SGraphNodeAI::GetTitle() const
{
return GraphNode ? GraphNode->GetNodeTitle(ENodeTitleType::FullTitle) : FText::GetEmpty();
}
FText SGraphNodeAI::GetDescription() const
{
UAIGraphNode* MyNode = CastChecked<UAIGraphNode>(GraphNode);
return MyNode ? MyNode->GetDescription() : FText::GetEmpty();
}
EVisibility SGraphNodeAI::GetDescriptionVisibility() const
{
// LOD this out once things get too small
TSharedPtr<SGraphPanel> MyOwnerPanel = GetOwnerPanel();
return (!MyOwnerPanel.IsValid() || MyOwnerPanel->GetCurrentLOD() > EGraphRenderingLOD::LowDetail) ? EVisibility::Visible : EVisibility::Collapsed;
}
void SGraphNodeAI::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() == EEdGraphPinDirection::EGPD_Input)
{
LeftNodeBox->AddSlot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.FillHeight(1.0f)
[
PinToAdd
];
InputPins.Add(PinToAdd);
}
else // Direction == EEdGraphPinDirection::EGPD_Output
{
RightNodeBox->AddSlot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.FillHeight(1.0f)
[
PinToAdd
];
OutputPins.Add(PinToAdd);
}
}
FReply SGraphNodeAI::OnMouseMove(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent)
{
if (MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton) && !(GEditor->bIsSimulatingInEditor || GEditor->PlayWorld))
{
//if we are holding mouse over a subnode
UAIGraphNode* TestNode = Cast<UAIGraphNode>(GraphNode);
if (TestNode && TestNode->IsSubNode())
{
const TSharedRef<SGraphPanel>& Panel = GetOwnerPanel().ToSharedRef();
const TSharedRef<SGraphNode>& Node = SharedThis(this);
return FReply::Handled().BeginDragDrop(FDragAIGraphNode::New(Panel, Node));
}
}
if (!MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton) && bDragMarkerVisible)
{
SetDragMarker(false);
}
return FReply::Unhandled();
}
FReply SGraphNodeAI::OnMouseDown(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent)
{
if (MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton))
{
UAIGraphNode* TestNode = Cast<UAIGraphNode>(GraphNode);
if (TestNode && TestNode->IsSubNode())
{
GetOwnerPanel()->SelectionManager.ClickedOnNode(GraphNode, MouseEvent);
return FReply::Handled();
}
}
return FReply::Unhandled();
}
TSharedPtr<SGraphNode> SGraphNodeAI::GetSubNodeUnderCursor(const FGeometry& WidgetGeometry, const FPointerEvent& MouseEvent)
{
TSharedPtr<SGraphNode> ResultNode;
// We just need to find the one WidgetToFind among our descendants.
TSet< TSharedRef<SWidget> > SubWidgetsSet;
for (int32 i = 0; i < SubNodes.Num(); i++)
{
SubWidgetsSet.Add(SubNodes[i].ToSharedRef());
}
TMap<TSharedRef<SWidget>, FArrangedWidget> Result;
FindChildGeometries(WidgetGeometry, SubWidgetsSet, Result);
if (Result.Num() > 0)
{
FArrangedChildren ArrangedChildren(EVisibility::Visible);
Result.GenerateValueArray(ArrangedChildren.GetInternalArray());
const int32 HoveredIndex = SWidget::FindChildUnderMouse(ArrangedChildren, MouseEvent);
if (HoveredIndex != INDEX_NONE)
{
ResultNode = StaticCastSharedRef<SGraphNode>(ArrangedChildren[HoveredIndex].Widget);
}
}
return ResultNode;
}
void SGraphNodeAI::SetDragMarker(bool bEnabled)
{
bDragMarkerVisible = bEnabled;
}
EVisibility SGraphNodeAI::GetDragOverMarkerVisibility() const
{
return bDragMarkerVisible ? EVisibility::Visible : EVisibility::Collapsed;
}
void SGraphNodeAI::OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
// Is someone dragging a node?
TSharedPtr<FDragNode> DragConnectionOp = DragDropEvent.GetOperationAs<FDragNode>();
if (DragConnectionOp.IsValid())
{
// Inform the Drag and Drop operation that we are hovering over this node.
TSharedPtr<SGraphNode> SubNode = GetSubNodeUnderCursor(MyGeometry, DragDropEvent);
DragConnectionOp->SetHoveredNode(SubNode.IsValid() ? SubNode : SharedThis(this));
UAIGraphNode* TestNode = Cast<UAIGraphNode>(GraphNode);
if (DragConnectionOp->IsValidOperation() && TestNode && TestNode->IsSubNode())
{
SetDragMarker(true);
}
}
SGraphNode::OnDragEnter(MyGeometry, DragDropEvent);
}
FReply SGraphNodeAI::OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
// Is someone dragging a node?
TSharedPtr<FDragNode> DragConnectionOp = DragDropEvent.GetOperationAs<FDragNode>();
if (DragConnectionOp.IsValid())
{
// Inform the Drag and Drop operation that we are hovering over this node.
TSharedPtr<SGraphNode> SubNode = GetSubNodeUnderCursor(MyGeometry, DragDropEvent);
DragConnectionOp->SetHoveredNode(SubNode.IsValid() ? SubNode : SharedThis(this));
}
return SGraphNode::OnDragOver(MyGeometry, DragDropEvent);
}
void SGraphNodeAI::OnDragLeave(const FDragDropEvent& DragDropEvent)
{
TSharedPtr<FDragNode> DragConnectionOp = DragDropEvent.GetOperationAs<FDragNode>();
if (DragConnectionOp.IsValid())
{
// Inform the Drag and Drop operation that we are not hovering any pins
DragConnectionOp->SetHoveredNode(TSharedPtr<SGraphNode>(NULL));
}
SetDragMarker(false);
SGraphNode::OnDragLeave(DragDropEvent);
}
FReply SGraphNodeAI::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
SetDragMarker(false);
TSharedPtr<FDragAIGraphNode> DragNodeOp = DragDropEvent.GetOperationAs<FDragAIGraphNode>();
if (DragNodeOp.IsValid())
{
if (!DragNodeOp->IsValidOperation())
{
return FReply::Handled();
}
const float DragTime = float(FPlatformTime::Seconds() - DragNodeOp->StartTime);
if (DragTime < 0.25f)
{
return FReply::Handled();
}
UAIGraphNode* MyNode = Cast<UAIGraphNode>(GraphNode);
if (MyNode == nullptr || MyNode->IsSubNode())
{
return FReply::Unhandled();
}
const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_DragDropNode", "Drag&Drop Node"));
bool bReorderOperation = true;
const TArray< TSharedRef<SGraphNode> >& DraggedNodes = DragNodeOp->GetNodes();
for (int32 Idx = 0; Idx < DraggedNodes.Num(); Idx++)
{
UAIGraphNode* DraggedNode = Cast<UAIGraphNode>(DraggedNodes[Idx]->GetNodeObj());
if (DraggedNode && DraggedNode->ParentNode)
{
if (DraggedNode->ParentNode != GraphNode)
{
bReorderOperation = false;
}
DraggedNode->ParentNode->RemoveSubNode(DraggedNode);
}
}
UAIGraphNode* DropTargetNode = DragNodeOp->GetDropTargetNode();
const int32 InsertIndex = MyNode->FindSubNodeDropIndex(DropTargetNode);
for (int32 Idx = 0; Idx < DraggedNodes.Num(); Idx++)
{
UAIGraphNode* DraggedTestNode = Cast<UAIGraphNode>(DraggedNodes[Idx]->GetNodeObj());
DraggedTestNode->Modify();
DraggedTestNode->ParentNode = MyNode;
MyNode->Modify();
MyNode->InsertSubNodeAt(DraggedTestNode, InsertIndex);
}
if (bReorderOperation)
{
UpdateGraphNode();
}
else
{
UAIGraph* MyGraph = MyNode->GetAIGraph();
if (MyGraph)
{
MyGraph->OnSubNodeDropped();
}
}
}
return SGraphNode::OnDrop(MyGeometry, DragDropEvent);
}
TSharedPtr<SToolTip> SGraphNodeAI::GetComplexTooltip()
{
return NULL;
}
FText SGraphNodeAI::GetPreviewCornerText() const
{
return FText::GetEmpty();
}
const FSlateBrush* SGraphNodeAI::GetNameIcon() const
{
return FAppStyle::GetBrush(TEXT("Graph.StateNode.Icon"));
}
void SGraphNodeAI::SetOwner(const TSharedRef<SGraphPanel>& OwnerPanel)
{
SGraphNode::SetOwner(OwnerPanel);
for (auto& ChildWidget : SubNodes)
{
if (ChildWidget.IsValid())
{
ChildWidget->SetOwner(OwnerPanel);
OwnerPanel->AttachGraphEvents(ChildWidget);
}
}
}
#undef LOCTEXT_NAMESPACE