A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
SDialogueGraphNode_Edge.cpp
Go to the documentation of this file.
1// Copyright Csaba Molnar, Daniel Butum. All Rights Reserved.
3
4#include "IDocumentation.h"
5#include "SGraphPanel.h"
6#include "Widgets/Images/SImage.h"
7#include "Widgets/SToolTip.h"
8#include "Widgets/Layout/SBox.h"
9
10#include "DialogueGraphNode.h"
12#include "DialogueStyle.h"
13
14#define LOCTEXT_NAMESPACE "DialogueEditor"
15
17// SDialogueGraphNode_Edge
18void SDialogueGraphNode_Edge::Construct(const FArguments& InArgs, UDialogueGraphNode_Edge* InNode)
19{
20 Super::Construct(Super::FArguments(), InNode);
22
24}
25
27// Begin SWidget interface
28void SDialogueGraphNode_Edge::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
29{
30 // NOTE: adding the OutputPin to the hover set will not work, only the input pin makes the connection
31 // to be hovered
32 TSharedPtr<SGraphPanel> GraphPanel = GetOwnerPanel();
33 GraphPanel->AddPinToHoverSet(DialogueGraphNode_Edge->GetInputPin());
34 Super::OnMouseEnter(MyGeometry, MouseEvent);
35}
36
37void SDialogueGraphNode_Edge::OnMouseLeave(const FPointerEvent& MouseEvent)
38{
39 TSharedPtr<SGraphPanel> GraphPanel = GetOwnerPanel();
40 GraphPanel->RemovePinFromHoverSet(DialogueGraphNode_Edge->GetInputPin());
41 Super::OnMouseLeave(MouseEvent);
42}
43// End SWidget interface
45
47// Begin SNodePanel::SNode Interface
48void SDialogueGraphNode_Edge::MoveTo(const FVector2D& NewPosition, FNodeSet& NodeFilter, bool bMarkDirty)
49{
50 // Ignored; position is set by the location of the attached nodes
51 Super::MoveTo(NewPosition, NodeFilter, bMarkDirty);
52}
53
54void SDialogueGraphNode_Edge::PerformSecondPassLayout(const TMap<UObject*, TSharedRef<SNode>>& NodeToWidgetLookup) const
55{
56 // Find the geometry of the nodes we're connecting
57 FGeometry StartGeom;
58 FGeometry EndGeom;
59 static constexpr int32 NodeIndex = 0;
60 static constexpr int32 NumberOfEdges = 1;
61
62 // Get the widgets the input/output node and find the geometries and the node inde and number of edges
65 const TSharedRef<SNode>* pPrevNodeWidget = NodeToWidgetLookup.Find(ParentNode);
66 const TSharedRef<SNode>* pNextNodeWidget = NodeToWidgetLookup.Find(ChildNode);
67 if (pPrevNodeWidget != nullptr && pNextNodeWidget != nullptr)
68 {
69 const TSharedRef<SNode>& PrevNodeWidget = *pPrevNodeWidget;
70 const TSharedRef<SNode>& NextNodeWidget = *pNextNodeWidget;
71
72 StartGeom = FGeometry(FVector2D(ParentNode->NodePosX, ParentNode->NodePosY), FVector2D::ZeroVector, PrevNodeWidget->GetDesiredSize(), 1.0f);
73 EndGeom = FGeometry(FVector2D(ChildNode->NodePosX, ChildNode->NodePosY), FVector2D::ZeroVector, NextNodeWidget->GetDesiredSize(), 1.0f);
74
75 // TODO do we need this? seems pointless as we can have only one edge between the same two nodes
76// // Get the input node graph edges, and filter those that only point to tour outputnode
77// TArray<UDialogueGraphNode_Edge*> GraphNode_Edges = InputNode->GetGraphNodeEdges();
78// GraphNode_Edges = GraphNode_Edges.FilterByPredicate([OutputNode](const UDialogueGraphNode_Edge* InEdge) -> bool
79// {
80// return InEdge->GetOutputNode() == OutputNode;
81// });
82// NodeIndex = GraphNode_Edges.IndexOfByKey(DialogueGraphNode_Edge);
83// NumberOfEdges = GraphNode_Edges.Num();
84 }
85
86 // Position Node
87 PositionBetweenTwoNodesWithOffset(StartGeom, EndGeom, NodeIndex, NumberOfEdges);
88}
89
90TArray<FOverlayWidgetInfo> SDialogueGraphNode_Edge::GetOverlayWidgets(bool bSelected, const FVector2D& WidgetSize) const
91{
92 // This is called after PerformSecondPassLayout, so the Edge should be in it's final Position Already
93 TArray<FOverlayWidgetInfo> Widgets;
94
95 if (ConditionOverlayWidget.IsValid())
96 {
98 {
99 FOverlayWidgetInfo Overlay(ConditionOverlayWidget);
100 // Position on the top/right of the node
101 const FVector2D& NewDesiredSize = ConditionOverlayWidget->GetDesiredSize();
102 Overlay.OverlayOffset = FVector2D(WidgetSize.X - NewDesiredSize.X / 2.0f, -NewDesiredSize.Y / 2.0f);
103 Widgets.Add(Overlay);
104 }
105 }
106
107 return Widgets;
108}
109// End SNodePanel::SNode Interface
111
113// Begin SGraphNode Interface
115{
117
118 // Fit to content
119 constexpr int WidthOverride = 16;
120 constexpr int HeightOverride = 16;
122 .OverlayBody(
123 SNew(SBox)
124 .HAlign(HAlign_Fill)
125 .VAlign(VAlign_Fill)
126 .WidthOverride(WidthOverride)
127 .HeightOverride(HeightOverride)
128 [
129 SNew(SImage)
131 ]
132 )
133 .ToolTipText(this, &Self::GetConditionOverlayTooltipText)
134 .Visibility(this, &Self::GetOverlayWidgetVisibility)
135 .OnGetBackgroundColor(this, &Self::GetOverlayWidgetBackgroundColor);
136
137 // Set Default tooltip
138 if (!SWidget::GetToolTip().IsValid())
139 {
140 const TSharedRef<SToolTip> DefaultToolTip = IDocumentation::Get()->CreateToolTip(TAttribute<FText>(this, &Super::GetNodeTooltip), nullptr,
141 GraphNode->GetDocumentationLink(), GraphNode->GetDocumentationExcerptName());
142 SetToolTip(DefaultToolTip);
143 }
144
145 ContentScale.Bind(this, &Super::GetContentScale);
146 GetOrAddSlot(ENodeZone::Center)
147 .HAlign(HAlign_Center)
148 .VAlign(VAlign_Center)
149 [
150 SNew(SOverlay)
151
152#if ENGINE_MINOR_VERSION >= 24
153 // >= 4.24
154 +SOverlay::Slot()
155 [
156 SNew(SImage)
157 .Image(FEditorStyle::GetBrush("Graph.TransitionNode.ColorSpill"))
158 .ColorAndOpacity(this, &Self::GetTransitionColor)
159 ]
160 +SOverlay::Slot()
161 [
162 SNew(SImage)
163 .Image(FEditorStyle::GetBrush("Graph.TransitionNode.Icon"))
164 ]
165#else
166 +SOverlay::Slot()
167 [
168 SNew(SImage)
169 .Image(FEditorStyle::GetBrush("Graph.TransitionNode.Body"))
170 ]
171 +SOverlay::Slot()
172 [
173 SNew(SImage)
174 .Image(FEditorStyle::GetBrush("Graph.TransitionNode.ColorSpill"))
175 .ColorAndOpacity(this, &Self::GetTransitionColor)
176 ]
177 +SOverlay::Slot()
178 [
179 SNew(SImage)
180 .Image(FEditorStyle::GetBrush("Graph.TransitionNode.Icon"))
181 ]
182 +SOverlay::Slot()
183 [
184 SNew(SImage)
185 .Image(FEditorStyle::GetBrush("Graph.TransitionNode.Gloss"))
186 ]
187#endif // ENGINE_MINOR_VERSION >= 24
188 ];
189}
190// End SGraphNode Interface
192
194// Begin own functions
195void SDialogueGraphNode_Edge::PositionBetweenTwoNodesWithOffset(const FGeometry& StartGeom, const FGeometry& EndGeom, int32 NodeIndex, int32 MaxNodes) const
196{
197 check(NodeIndex >= 0);
198 check(MaxNodes > 0);
199
200 // Get a reasonable seed point (halfway between the boxes)
201 const FVector2D StartCenter = FGeometryHelper::CenterOf(StartGeom);
202 const FVector2D EndCenter = FGeometryHelper::CenterOf(EndGeom);
203 const FVector2D SeedPoint = (StartCenter + EndCenter) / 2.0f;
204
205 // Find the (approximate) closest points between the two boxes
206 const FVector2D StartAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(StartGeom, SeedPoint);
207 const FVector2D EndAnchorPoint = FGeometryHelper::FindClosestPointOnGeom(EndGeom, SeedPoint);
208
209 // Position ourselves halfway along the connecting line between the nodes, elevated away perpendicular to the direction of the line
210 static constexpr float Height = 30.0f;
211 const FVector2D DesiredNodeSize = GetDesiredSize();
212
213 FVector2D DeltaPos(EndAnchorPoint - StartAnchorPoint);
214 if (DeltaPos.IsNearlyZero())
215 {
216 DeltaPos = FVector2D(10.0f, 0.0f);
217 }
218
219 const FVector2D Normal = FVector2D(DeltaPos.Y, -DeltaPos.X).GetSafeNormal();
220 const FVector2D NewCenter = StartAnchorPoint + (0.5f * DeltaPos) + (Height * Normal);
221 const FVector2D DeltaNormal = DeltaPos.GetSafeNormal();
222
223 // TODO do we need this?
224 // Calculate node offset in the case of multiple transitions between the same two nodes
225 // MultiNodeOffset: the offset where 0 is the centre of the transition, -1 is 1 <size of node>
226 // towards the PrevStateNode and +1 is 1 <size of node> towards the NextStateNode.
227 static constexpr float MultiNodeSpace = 0.2f; // Space between multiple edge nodes (in units of <size of node> )
228 static constexpr float MultiNodeStep = 1.f + MultiNodeSpace; // Step between node centres (Size of node + size of node spacer)
229
230 const float MultiNodeStart = -((MaxNodes - 1) * MultiNodeStep) / 2.f;
231 const float MultiNodeOffset = MultiNodeStart + (NodeIndex * MultiNodeStep);
232
233 // Now we need to adjust the new center by the node size, zoom factor and multi node offset
234 const FVector2D NewCorner = NewCenter - (0.5f * DesiredNodeSize) + (DeltaNormal * MultiNodeOffset * DesiredNodeSize.Size());
235
236 GraphNode->NodePosX = NewCorner.X;
237 GraphNode->NodePosY = NewCorner.Y;
238}
239
241{
242 return LOCTEXT("NodeConditionTooltip", "Edge has conditions.\nOnly if these conditions are satisfied then this edge is considered as an option.");
243}
244
246{
247 // LOD this out once things get too small
248 TSharedPtr<SGraphPanel> MyOwnerPanel = GetOwnerPanel();
249 return !MyOwnerPanel.IsValid() || MyOwnerPanel->GetCurrentLOD() > EGraphRenderingLOD::LowDetail ? EVisibility::Visible : EVisibility::Collapsed;
250}
251
252// End own functions
254
255#undef LOCTEXT_NAMESPACE
static TSharedPtr< ISlateStyle > Get()
static const FName PROPERTY_QuestionMarkIcon
const UDlgSystemSettings * Settings
void Construct(const FArguments &InArgs, UDialogueGraphNode_Base *InNode)
void MoveTo(const FVector2D &NewPosition, FNodeSet &NodeFilter, bool bMarkDirty=true) override
void PerformSecondPassLayout(const TMap< UObject *, TSharedRef< SNode > > &InNodeToWidgetLookup) const override
void OnMouseLeave(const FPointerEvent &MouseEvent) override
TArray< FOverlayWidgetInfo > GetOverlayWidgets(bool bSelected, const FVector2D &WidgetSize) const override
FSlateColor GetTransitionColor() const
TSharedPtr< SWidget > ConditionOverlayWidget
void Construct(const FArguments &InArgs, UDialogueGraphNode_Edge *InNode)
UDialogueGraphNode_Edge * DialogueGraphNode_Edge
FSlateColor GetOverlayWidgetBackgroundColor(bool bHovered) const
EVisibility GetOverlayWidgetVisibility() const
void OnMouseEnter(const FGeometry &MyGeometry, const FPointerEvent &MouseEvent) override
void PositionBetweenTwoNodesWithOffset(const FGeometry &StartGeom, const FGeometry &EndGeom, int32 NodeIndex, int32 MaxNodes) const
UEdGraphPin * GetInputPin() const
UDialogueGraphNode * GetChildNode() const
UDialogueGraphNode * GetParentNode() const
bool bShowEdgeHasConditionsIcon
UPROPERTY(Category = "Graph Edge", Config, EditAnywhere)