A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
SDialogueGraphPin.cpp
Go to the documentation of this file.
1// Copyright Csaba Molnar, Daniel Butum. All Rights Reserved.
2#include "SDialogueGraphPin.h"
3
4#include "ScopedTransaction.h"
5#include "SGraphPanel.h"
6#include "SGraphNode.h"
7#include "Math/UnitConversion.h"
8
10#include "DialogueGraphNode.h"
11
12#define LOCTEXT_NAMESPACE "SDialogueGraphPin"
13
15// SDialogueGraphPin
16void SDialogueGraphPin::Construct(const FArguments& InArgs, UEdGraphPin* InPin)
17{
18 check(InPin);
19 SetCursor(EMouseCursor::Default);
20 bShowLabel = true;
21 GraphPinObj = InPin;
22
23 const UEdGraphSchema* Schema = GraphPinObj->GetSchema();
24 check(Schema);
25
26 SBorder::Construct(SBorder::FArguments()
27 .BorderImage(this, &Self::GetPinBorder)
28 .BorderBackgroundColor(this, &Self::GetPinColor)
29 .OnMouseButtonDown(this, &Self::OnPinMouseDown)
30 .Cursor(this, &Self::GetPinCursor)
31 .Padding(FMargin(10.f))
32 );
33
34 TAttribute<FText> ToolTipAttribute = TAttribute<FText>::Create(TAttribute<FText>::FGetter::CreateSP(this, &Self::GetTooltipText));
35 SetToolTipText(ToolTipAttribute);
36}
37
39// Begin SGraphPin Interface
40FReply SDialogueGraphPin::OnPinMouseDown(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent)
41{
42 bIsMovingLinks = false;
43 if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && IsEditingEnabled())
44 {
45 if (GraphPinObj->LinkedTo.Num() > 0)
46 {
47 if (MouseEvent.IsAltDown())
48 {
49 return OnAltAndLeftMouseButtonDown(SenderGeometry, MouseEvent);
50 }
51
52 if (MouseEvent.IsControlDown())
53 {
54 return OnCtrlAndLeftMouseButtonDown(SenderGeometry, MouseEvent);
55 }
56 }
57
58 // Regular drag operation, to create new links
59 if (!GraphPinObj->bNotConnectable)
60 {
61 // Start a drag-drop on the pin
62 TSharedPtr<SGraphNode> ThisOwnerNodeWidget = OwnerNodePtr.Pin();
63 if (ensure(ThisOwnerNodeWidget.IsValid()))
64 {
65 TArray<TSharedRef<SGraphPin>> DragPinArray;
66 DragPinArray.Add(SharedThis(this));
67 return FReply::Handled().BeginDragDrop(SpawnPinDragEvent(ThisOwnerNodeWidget->GetOwnerPanel().ToSharedRef(), DragPinArray));
68 }
69
70 return FReply::Unhandled();
71 }
72
73 // It's not connectible, but we don't want anything above us to process this left click.
74 return FReply::Handled();
75 }
76 return FReply::Unhandled();
77}
78// End SGraphPin Interface
80
82// Begin SWidget interface
83FReply SDialogueGraphPin::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
84{
85 return FReply::Unhandled();
86}
87
88FReply SDialogueGraphPin::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
89{
90 return Super::OnMouseMove(MyGeometry, MouseEvent);
91}
92
93void SDialogueGraphPin::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
94{
95 if (!bIsHovered && GraphPinObj && !GraphPinObj->IsPendingKill() && GraphPinObj->GetOuter())
96 {
97 TSharedPtr<SGraphPanel> OwnerPanelPtr = OwnerNodePtr.Pin()->GetOwnerPanel();
98 check(OwnerPanelPtr.IsValid());
99
100 // Will be removed in Super::OnMouseLeave
101 // Add current pin
102 HoverPinSet.Add(GraphPinObj);
103 OwnerPanelPtr->AddPinToHoverSet(GraphPinObj);
104
105 if (GraphPinObj->LinkedTo.Num() > 0)
106 {
107 // Pin belongs to an actual node
108 if (UDialogueGraphNode* GraphNode = Cast<UDialogueGraphNode>(GraphPinObj->GetOwningNode()))
109 {
110 if (GraphPinObj->Direction == EGPD_Output)
111 {
112 // output pin mark all
113 for (UDialogueGraphNode* ChildNode : GraphNode->GetChildNodes())
114 {
115 HoverPinSet.Add(ChildNode->GetInputPin());
116 OwnerPanelPtr->AddPinToHoverSet(ChildNode->GetInputPin());
117 }
118 }
119 else
120 {
121 // input pin
122 }
123 }
124 }
125 }
126
127 SCompoundWidget::OnMouseEnter(MyGeometry, MouseEvent);
128}
129
130void SDialogueGraphPin::OnMouseLeave(const FPointerEvent& MouseEvent)
131{
132 Super::OnMouseLeave(MouseEvent);
133}
134
135//
136// DRAG AND DROP (DragDrop)
137// Used by the TargetPin
138//
139void SDialogueGraphPin::OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
140{
141 Super::OnDragEnter(MyGeometry, DragDropEvent);
142}
143
144void SDialogueGraphPin::OnDragLeave(const FDragDropEvent& DragDropEvent)
145{
146 Super::OnDragLeave(DragDropEvent);
147}
148
149FReply SDialogueGraphPin::OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
150{
151 return Super::OnDragOver(MyGeometry, DragDropEvent);
152}
153
154FReply SDialogueGraphPin::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
155{
156 return Super::OnDrop(MyGeometry, DragDropEvent);
157}
158// End SWidget interface
160
162// Begin own functions
163FReply SDialogueGraphPin::OnAltAndLeftMouseButtonDown(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent)
164{
165 // Alt-Left clicking will break that pin, if alt clicking on the wire, otherwise it will break all pins
166 TSharedPtr<SGraphNode> ThisOwnerNodeWidget = OwnerNodePtr.Pin();
167 check(ThisOwnerNodeWidget.IsValid());
168 const UDialogueGraphSchema* Schema = CastChecked<UDialogueGraphSchema>(GraphPinObj->GetSchema());
169 const FVector2D& MouseLocation = MouseEvent.GetScreenSpacePosition();
170
171 // Is the click inside the node?
172 if (FDialogueEditorUtilities::IsPointInsideGeometry(MouseLocation, ThisOwnerNodeWidget->GetCachedGeometry()))
173 {
174 // break all links
175 Schema->BreakPinLinks(*GraphPinObj, true);
176 }
177 else
178 {
179 // Find the appropriate pin, if there are multiple children
180 UEdGraphPin* ToPin = GraphPinObj->LinkedTo[0];
181 if (GraphPinObj->LinkedTo.Num() > 1)
182 {
183 ToPin = GetBestLinkedToPinFromSplineMousePosition(MouseLocation);
184 }
185
186 Schema->BreakLinkTo(GraphPinObj, ToPin, true);
187 }
188
189 return FReply::Handled();
190}
191
192FReply SDialogueGraphPin::OnCtrlAndLeftMouseButtonDown(const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent)
193{
194 // Control-Left clicking will break all existing connections to a pin
195 // Note: that for some nodes, this can cause reconstruction. In that case, pins we had previously linked to may now be destroyed.
196 // So the break MUST come after the SpawnPinDragEvent(), since that acquires handles from PinArray (the pins need to be
197 // around for us to construct valid handles from).
198 TSharedPtr<SGraphNode> ThisOwnerNodeWidget = OwnerNodePtr.Pin();
199 check(ThisOwnerNodeWidget.IsValid());
200 TSharedPtr<SGraphPanel> OwnerPanelPtr = ThisOwnerNodeWidget->GetOwnerPanel();
201 check(OwnerPanelPtr.IsValid());
202
203 TArray<TSharedRef<SGraphPin>> DragPinArray;
204 DragPinArray.Add(SharedThis(this));
205
206 // Find the appropriate pin, if there are multiple children
207 UEdGraphPin* ToPin = GraphPinObj->LinkedTo[0];
208 if (GraphPinObj->LinkedTo.Num() > 1)
209 {
210 ToPin = GetBestLinkedToPinFromSplineMousePosition(MouseEvent.GetScreenSpacePosition());
211 }
212
213 // Start drag operation
214 // Kill the current connection
215 if (UDialogueGraphNode_Edge* GraphEdge = Cast<UDialogueGraphNode_Edge>(ToPin->GetOwningNode()))
216 {
217 // true almost all the times, but just to be sure in the future
218 FDialogueEditorUtilities::SetLastTargetGraphEdgeBeforeDrag(GraphEdge->GetGraph(), GraphEdge);
219 }
220
221 const UDialogueGraphSchema* Schema = CastChecked<UDialogueGraphSchema>(GraphPinObj->GetSchema());
222 Schema->BreakLinkTo(GraphPinObj, ToPin, true);
223
224 bIsMovingLinks = true;
225 // Handled by SGraphPanel::OnDrop
226 return FReply::Handled().BeginDragDrop(SpawnPinDragEvent(ThisOwnerNodeWidget->GetOwnerPanel().ToSharedRef(), DragPinArray));
227}
228
230{
231 FText HoverText = FText::GetEmpty();
232 return HoverText;
233 // TODO find a way to only show this when hovering over the wire but not over the node
234// const UEdGraphNode* GraphNode = GraphPinObj && !GraphPinObj->IsPendingKill() ? GraphPinObj->GetOwningNodeUnchecked() : nullptr;
235// if (GraphNode != nullptr)
236// {
237// FString HoverStr;
238// GraphNode->GetPinHoverText(*GraphPinObj, /*out*/HoverStr);
239// if (!HoverStr.IsEmpty())
240// {
241// HoverText = FText::FromString(HoverStr);
242// }
243// }
244//
245// return HoverText;
246}
247
248UEdGraphPin* SDialogueGraphPin::GetBestLinkedToPinFromSplineMousePosition(const FVector2D& MousePosition) const
249{
250 /*
251 ASSUMPTION: that the MousePosition is on a spline (wire) or near a wire
252 Notation:
253 M - The position of the mouse Vector (aka MousePosition)
254 P - ThisGraphNode Vector location (aka ThisGraphNodeClosestPosition)
255 C - GraphNode child location (aka ClosestPointOnChild)
256
257 The (GraphEdge, GraphNode child) pair with the angle(MC, MP) closest to 180 degrees corresponds to this MousePosition.
258 Basically this tries to find the edge that has MousePosition on its spline/wire.
259
260 Crude illustration:
261 ____ ____
262 | | E1 | |
263 | P |----M------------->| C |
264 |___| |___|
265 |
266 | E2
267 |
268 _V__
269 | |
270 | D |
271 |___|
272 */
273
274 const UDialogueGraphNode* ThisGraphNode = CastChecked<UDialogueGraphNode>(GraphPinObj->GetOwningNode());
275 TSharedPtr<SGraphNode> ThisGraphNodeWidget = OwnerNodePtr.Pin();
276 check(ThisGraphNodeWidget.IsValid());
277
278 // Find P and MP
279 const FVector2D ThisGraphNodeClosestPosition = FGeometryHelper::FindClosestPointOnGeom(
280 ThisGraphNodeWidget->GetCachedGeometry(),
281 MousePosition
282 );
283 const FVector2D MP = (ThisGraphNodeClosestPosition - MousePosition).GetSafeNormal();
284
285 // Iterate over all edges, find the best one
286 const TArray<UDialogueGraphNode_Edge*> ChildGraphEdges = ThisGraphNode->GetChildEdgeNodes();
287 int32 BestEdgeIndex = 0;
288
289 // Dot product is always positive here, because we do not care about direction
290 float BestDotProduct = 0.0f;
291 const int32 ChildNum = ChildGraphEdges.Num();
292 for (int32 ChildIndex = 0; ChildIndex < ChildNum; ChildIndex++)
293 {
294 UDialogueGraphNode_Edge* ChildEdge = ChildGraphEdges[ChildIndex];
295 TSharedPtr<SGraphNode> ChildNodeWidget = ChildEdge->GetChildNode()->GetNodeWidget();
296 check(ChildNodeWidget.IsValid());
297
298 // Find C (aka ClosestPointOnChild)
299 const FVector2D ClosestPointOnChild = FGeometryHelper::FindClosestPointOnGeom(ChildNodeWidget->GetCachedGeometry(), MousePosition);
300
301 // Find angle between vectors
302 const FVector2D MC = (ClosestPointOnChild - MousePosition).GetSafeNormal();
303 const float DotProduct = FMath::Abs(FVector2D::DotProduct(MC, MP));
304 if (DotProduct > BestDotProduct)
305 {
306 // found new angle approaching 180 degrees
307 BestDotProduct = DotProduct;
308 BestEdgeIndex = ChildIndex;
309 }
310 }
311
312 return GraphPinObj->LinkedTo[BestEdgeIndex];;
313}
314// End own functions
316
317
318#undef LOCTEXT_NAMESPACE
static void SetLastTargetGraphEdgeBeforeDrag(const UEdGraph *Graph, UDialogueGraphNode_Edge *InEdge)
static bool IsPointInsideGeometry(const FVector2D &TestPoint, const FGeometry &Geometry)
FReply OnDrop(const FGeometry &MyGeometry, const FDragDropEvent &DragDropEvent) override
FText GetTooltipText() const
void OnDragLeave(const FDragDropEvent &DragDropEvent) override
void OnMouseEnter(const FGeometry &MyGeometry, const FPointerEvent &MouseEvent) override
FReply OnCtrlAndLeftMouseButtonDown(const FGeometry &SenderGeometry, const FPointerEvent &MouseEvent)
FReply OnMouseButtonUp(const FGeometry &MyGeometry, const FPointerEvent &MouseEvent) override
void OnMouseLeave(const FPointerEvent &MouseEvent) override
const FSlateBrush * GetPinBorder() const
FReply OnDragOver(const FGeometry &MyGeometry, const FDragDropEvent &DragDropEvent) override
FSlateColor GetPinColor() const override
FReply OnPinMouseDown(const FGeometry &SenderGeometry, const FPointerEvent &MouseEvent) override
FReply OnMouseMove(const FGeometry &MyGeometry, const FPointerEvent &MouseEvent) override
UEdGraphPin * GetBestLinkedToPinFromSplineMousePosition(const FVector2D &MousePosition) const
void Construct(const FArguments &InArgs, UEdGraphPin *InPin)
FReply OnAltAndLeftMouseButtonDown(const FGeometry &SenderGeometry, const FPointerEvent &MouseEvent)
void OnDragEnter(const FGeometry &MyGeometry, const FDragDropEvent &DragDropEvent) override
TSharedPtr< SGraphNode > GetNodeWidget() const
UDialogueGraphNode * GetChildNode() const
TArray< UDialogueGraphNode_Edge * > GetChildEdgeNodes(bool bCheckParent=true) const
void BreakPinLinks(UEdGraphPin &TargetPin, bool bSendsNodeNotifcation) const override
void BreakLinkTo(UEdGraphPin *TargetPin, UEdGraphPin *ToPin, bool bSendsNodeNotifcation) const