// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. #include "FConnectionDrawingPolicy_ComboActionGraph.h" #include "ComboInputEditor.h" #include "Ed/EdComboActionGraphEdge.h" #include "Ed/EdComboActionGraphNode.h" #include "Settings/ComboActionGraphEditorSettings.h" #include "Settings/FComboActionGraphEditorStyle.h" DEFINE_LOG_CATEGORY(LogConnectionDrawingPolicy_ComboActionGraph); FConnectionDrawingPolicy_ComboActionGraph::FConnectionDrawingPolicy_ComboActionGraph(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect &InClippingRect, FSlateWindowElementList &InDrawElements, UEdGraph *InGraphObj) : FKismetConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj) , GraphObj(InGraphObj) { if (const UComboActionGraphEditorSettings* GraphEditorSettings = GetMutableDefault()) { FComboInputEditorModule &ComboInputEditorModule = FComboInputEditorModule::Get(); switch (GraphEditorSettings->GetArrowType()) { case EComboActionArrowType::SimpleArrow: ArrowImage = ComboInputEditorModule.ComboActionGraphEditorStyleSet->GetBrush(TEXT("MDSStyleSet.Graph.SimpleArrow")); break; case EComboActionArrowType::HollowArrow: ArrowImage = ComboInputEditorModule.ComboActionGraphEditorStyleSet->GetBrush(TEXT("MDSStyleSet.Graph.HollowArrow")); break; case EComboActionArrowType::FancyArrow: ArrowImage = ComboInputEditorModule.ComboActionGraphEditorStyleSet->GetBrush(TEXT("MDSStyleSet.Graph.FancyArrow")); break; case EComboActionArrowType::Bubble: ArrowImage = ComboInputEditorModule.ComboActionGraphEditorStyleSet->GetBrush(TEXT("MDSStyleSet.Graph.Bubble")); break; case EComboActionArrowType::None: default: ArrowImage = nullptr; } } else { ArrowImage = FAppStyle::GetBrush( TEXT("GenericPlay") ); } ArrowRadius = ArrowImage ? ArrowImage->ImageSize * ZoomFactor * 0.5f : FVector2D(0.f); MidpointImage = nullptr; MidpointRadius = FVector2D::ZeroVector; HoverDeemphasisDarkFraction = 0.8f; BubbleImage = FAppStyle::GetBrush( TEXT("Graph.Arrow") ); } void FConnectionDrawingPolicy_ComboActionGraph::DetermineWiringStyle(UEdGraphPin *OutputPin, UEdGraphPin *InputPin, FConnectionParams &Params) { Params.AssociatedPin1 = OutputPin; Params.AssociatedPin2 = InputPin; const UComboActionGraphEditorSettings *MounteaDialogueGraphEditorSettings = GetDefault(); if (MounteaDialogueGraphEditorSettings) { Params.WireThickness = MounteaDialogueGraphEditorSettings->GetWireWidth(); } else { Params.WireThickness = 1.f; } const bool bDeemphasizeUnhoveredPins = HoveredPins.Num() > 0; if (bDeemphasizeUnhoveredPins) { ApplyHoverDeemphasis(OutputPin, InputPin, /*inout*/ Params.WireThickness, /*inout*/ Params.WireColor); } } void FConnectionDrawingPolicy_ComboActionGraph::Draw(TMap, FArrangedWidget> &InPinGeometries, FArrangedChildren &ArrangedNodes) { // Build an acceleration structure to quickly find geometry for the nodes NodeWidgetMap.Empty(); for (int32 NodeIndex = 0; NodeIndex < ArrangedNodes.Num(); ++NodeIndex) { FArrangedWidget& CurWidget = ArrangedNodes[NodeIndex]; TSharedRef ChildNode = StaticCastSharedRef(CurWidget.Widget); NodeWidgetMap.Add(ChildNode->GetNodeObj(), NodeIndex); } // Now draw FConnectionDrawingPolicy::Draw(InPinGeometries, ArrangedNodes); } void FConnectionDrawingPolicy_ComboActionGraph::DrawSplineWithArrow(const FGeometry &StartGeom, const FGeometry &EndGeom, const FConnectionParams &Params) { // Get a reasonable seed point (halfway between the boxes) const FVector2D StartCenter = FGeometryHelper::CenterOf(StartGeom); const FVector2D EndCenter = FGeometryHelper::CenterOf(EndGeom); const FVector2D SeedPoint = (StartCenter + EndCenter) * 0.5f; // Find the (approximate) closest points between the two boxes const FVector2D StartAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(StartGeom, SeedPoint); const FVector2D EndAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(EndGeom, SeedPoint); this->DrawSplineWithArrow(StartAnchorPoint, EndAnchorPoint, Params); } void FConnectionDrawingPolicy_ComboActionGraph::DrawSplineWithArrow(const FVector2D &StartPoint, const FVector2D &EndPoint, const FConnectionParams &Params) { // bUserFlag1 indicates that we need to reverse the direction of connection (used by debugger) const FVector2D &P0 = Params.bUserFlag1 ? EndPoint : StartPoint; const FVector2D &P1 = Params.bUserFlag1 ? StartPoint : EndPoint; UE_LOG(LogConnectionDrawingPolicy_ComboActionGraph, Verbose, TEXT("%s I %s"), *P0.ToString(), *P1.ToString()); FConnectionParams NewParams = Params; //NewParams.bDrawBubbles = true; if (const UComboActionGraphEditorSettings *MounteaDialogueGraphEditorSettings = GetMutableDefault()) { NewParams.WireThickness = MounteaDialogueGraphEditorSettings->GetWireWidth(); } Internal_DrawLineWithArrow(P0, P1, NewParams); } void FConnectionDrawingPolicy_ComboActionGraph::DrawPreviewConnector(const FGeometry &PinGeometry, const FVector2D &StartPoint, const FVector2D &EndPoint, UEdGraphPin* Pin) { FConnectionParams Params; DetermineWiringStyle(Pin, nullptr, /*inout*/ Params); if (Pin->Direction == EEdGraphPinDirection::EGPD_Output) { DrawSplineWithArrow(FGeometryHelper::FindClosestPointOnGeom(PinGeometry, EndPoint), EndPoint, Params); } else { DrawSplineWithArrow(FGeometryHelper::FindClosestPointOnGeom(PinGeometry, StartPoint), StartPoint, Params); } } FVector2D FConnectionDrawingPolicy_ComboActionGraph::ComputeSplineTangent(const FVector2D &Start, const FVector2D &End) const { const FVector2D Delta = End - Start; const FVector2D NormDelta = Delta.GetSafeNormal(); return NormDelta; } void FConnectionDrawingPolicy_ComboActionGraph::DetermineLinkGeometry(FArrangedChildren &ArrangedNodes, TSharedRef &OutputPinWidget, UEdGraphPin *OutputPin, UEdGraphPin *InputPin, FArrangedWidget *&StartWidgetGeometry, FArrangedWidget *&EndWidgetGeometry) { if (UEdComboActionGraphEdge *EdgeNode = Cast(InputPin->GetOwningNode())) { UEdComboActionGraphNode* Start = EdgeNode->GetStartNode(); UEdComboActionGraphNode* End = EdgeNode->GetEndNode(); if (Start != nullptr && End != nullptr) { int32* StartNodeIndex = NodeWidgetMap.Find(Start); int32* EndNodeIndex = NodeWidgetMap.Find(End); if (StartNodeIndex != nullptr && EndNodeIndex != nullptr) { StartWidgetGeometry = &(ArrangedNodes[*StartNodeIndex]); EndWidgetGeometry = &(ArrangedNodes[*EndNodeIndex]); } } } else { StartWidgetGeometry = PinGeometries->Find(OutputPinWidget); if (TSharedPtr* pTargetWidget = PinToPinWidgetMap.Find(InputPin)) { TSharedRef InputWidget = (*pTargetWidget).ToSharedRef(); EndWidgetGeometry = PinGeometries->Find(InputWidget); } } } void FConnectionDrawingPolicy_ComboActionGraph::Internal_DrawLineWithArrow(const FVector2D &StartAnchorPoint, const FVector2D &EndAnchorPoint, const FConnectionParams &Params) { const float LineSeparationAmount = 4.5f; const FVector2D DeltaPos = EndAnchorPoint - StartAnchorPoint; const FVector2D UnitDelta = DeltaPos.GetSafeNormal(); const FVector2D Normal = FVector2D(DeltaPos.Y, -DeltaPos.X).GetSafeNormal(); // Come up with the final start/end points const FVector2D DirectionBias = Normal * LineSeparationAmount; const FVector2D LengthBias = ArrowRadius.X * UnitDelta; const FVector2D StartPoint = StartAnchorPoint + DirectionBias + LengthBias; const FVector2D EndPoint = EndAnchorPoint + DirectionBias - LengthBias; // Draw a line/spline DrawConnection(WireLayerID, StartPoint, EndPoint, Params); // Draw the arrow if (ArrowImage) { const FVector2D ArrowDrawPos = EndPoint - ArrowRadius; const float AngleInRadians = FMath::Atan2(DeltaPos.Y, DeltaPos.X); FSlateDrawElement::MakeRotatedBox( DrawElementsList, ArrowLayerID, FPaintGeometry(ArrowDrawPos, ArrowImage->ImageSize * ZoomFactor, ZoomFactor), ArrowImage, ESlateDrawEffect::None, AngleInRadians, TOptional(), FSlateDrawElement::RelativeToElement, Params.WireColor ); } }