// 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::New(const TSharedRef& InGraphPanel, const TSharedRef& InDraggedNode) { TSharedRef 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::New(const TSharedRef& InGraphPanel, const TArray< TSharedRef >& InDraggedNodes) { TSharedRef 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(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 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 SubNodeWidget) { SubNodes.Add(SubNodeWidget); } FText SGraphNodeAI::GetTitle() const { return GraphNode ? GraphNode->GetNodeTitle(ENodeTitleType::FullTitle) : FText::GetEmpty(); } FText SGraphNodeAI::GetDescription() const { UAIGraphNode* MyNode = CastChecked(GraphNode); return MyNode ? MyNode->GetDescription() : FText::GetEmpty(); } EVisibility SGraphNodeAI::GetDescriptionVisibility() const { // LOD this out once things get too small TSharedPtr MyOwnerPanel = GetOwnerPanel(); return (!MyOwnerPanel.IsValid() || MyOwnerPanel->GetCurrentLOD() > EGraphRenderingLOD::LowDetail) ? EVisibility::Visible : EVisibility::Collapsed; } void SGraphNodeAI::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() == 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(GraphNode); if (TestNode && TestNode->IsSubNode()) { const TSharedRef& Panel = GetOwnerPanel().ToSharedRef(); const TSharedRef& 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(GraphNode); if (TestNode && TestNode->IsSubNode()) { GetOwnerPanel()->SelectionManager.ClickedOnNode(GraphNode, MouseEvent); return FReply::Handled(); } } return FReply::Unhandled(); } TSharedPtr SGraphNodeAI::GetSubNodeUnderCursor(const FGeometry& WidgetGeometry, const FPointerEvent& MouseEvent) { TSharedPtr ResultNode; // We just need to find the one WidgetToFind among our descendants. TSet< TSharedRef > SubWidgetsSet; for (int32 i = 0; i < SubNodes.Num(); i++) { SubWidgetsSet.Add(SubNodes[i].ToSharedRef()); } TMap, 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(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 DragConnectionOp = DragDropEvent.GetOperationAs(); if (DragConnectionOp.IsValid()) { // Inform the Drag and Drop operation that we are hovering over this node. TSharedPtr SubNode = GetSubNodeUnderCursor(MyGeometry, DragDropEvent); DragConnectionOp->SetHoveredNode(SubNode.IsValid() ? SubNode : SharedThis(this)); UAIGraphNode* TestNode = Cast(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 DragConnectionOp = DragDropEvent.GetOperationAs(); if (DragConnectionOp.IsValid()) { // Inform the Drag and Drop operation that we are hovering over this node. TSharedPtr SubNode = GetSubNodeUnderCursor(MyGeometry, DragDropEvent); DragConnectionOp->SetHoveredNode(SubNode.IsValid() ? SubNode : SharedThis(this)); } return SGraphNode::OnDragOver(MyGeometry, DragDropEvent); } void SGraphNodeAI::OnDragLeave(const FDragDropEvent& DragDropEvent) { TSharedPtr DragConnectionOp = DragDropEvent.GetOperationAs(); if (DragConnectionOp.IsValid()) { // Inform the Drag and Drop operation that we are not hovering any pins DragConnectionOp->SetHoveredNode(TSharedPtr(NULL)); } SetDragMarker(false); SGraphNode::OnDragLeave(DragDropEvent); } FReply SGraphNodeAI::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) { SetDragMarker(false); TSharedPtr DragNodeOp = DragDropEvent.GetOperationAs(); 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(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 >& DraggedNodes = DragNodeOp->GetNodes(); for (int32 Idx = 0; Idx < DraggedNodes.Num(); Idx++) { UAIGraphNode* DraggedNode = Cast(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(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 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& OwnerPanel) { SGraphNode::SetOwner(OwnerPanel); for (auto& ChildWidget : SubNodes) { if (ChildWidget.IsValid()) { ChildWidget->SetOwner(OwnerPanel); OwnerPanel->AttachGraphEvents(ChildWidget); } } } #undef LOCTEXT_NAMESPACE