2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "GraphEditorCommon.h"
# include "NodeFactory.h"
# include "Editor/UnrealEd/Public/DragAndDrop/ActorDragDropGraphEdOp.h"
# include "Editor/UnrealEd/Public/DragAndDrop/AssetDragDropOp.h"
# include "Editor/UnrealEd/Public/DragAndDrop/LevelDragDropOp.h"
2014-11-14 18:23:41 -05:00
2014-03-14 14:13:41 -04:00
# include "ConnectionDrawingPolicy.h"
2014-11-14 18:23:41 -05:00
# include "BlueprintConnectionDrawingPolicy.h"
# include "AnimGraphConnectionDrawingPolicy.h"
# include "SoundCueGraphConnectionDrawingPolicy.h"
# include "MaterialGraphConnectionDrawingPolicy.h"
# include "StateMachineConnectionDrawingPolicy.h"
2014-03-14 14:13:41 -04:00
# include "AssetSelection.h"
# include "ComponentAssetBroker.h"
# include "KismetNodes/KismetNodeInfoContext.h"
# include "GraphDiffControl.h"
2014-04-24 08:49:31 -04:00
# include "AnimationGraphSchema.h"
2014-04-24 14:34:01 -04:00
# include "AnimationStateMachineSchema.h"
2014-04-24 08:49:31 -04:00
2014-03-14 14:13:41 -04:00
DEFINE_LOG_CATEGORY_STATIC ( LogGraphPanel , Log , All ) ;
2014-10-14 22:50:06 -04:00
DECLARE_CYCLE_STAT ( TEXT ( " OnPaint SGraphPanel " ) , STAT_SlateOnPaint_SGraphPanel , STATGROUP_Slate ) ;
2014-04-25 05:03:13 -04:00
2014-04-26 09:55:34 -04:00
SGraphPanel : : FGraphPinHandle : : FGraphPinHandle ( UEdGraphPin * InPin )
2014-04-25 05:03:13 -04:00
{
2014-04-26 09:55:34 -04:00
if ( auto * Node = InPin - > GetOwningNode ( ) )
2014-04-25 05:03:13 -04:00
{
2014-04-26 09:55:34 -04:00
PinName = InPin - > PinName ;
NodeGuid = Node - > NodeGuid ;
}
}
TSharedPtr < SGraphPin > SGraphPanel : : FGraphPinHandle : : FindInGraphPanel ( const SGraphPanel & InPanel ) const
{
// First off, find the node
TSharedPtr < SGraphNode > GraphNode = InPanel . GetNodeWidgetFromGuid ( NodeGuid ) ;
if ( GraphNode . IsValid ( ) )
{
UEdGraphNode * Node = GraphNode - > GetNodeObj ( ) ;
UEdGraphPin * Pin = Node - > FindPin ( PinName ) ;
if ( Pin )
2014-04-25 05:03:13 -04:00
{
2014-04-26 09:55:34 -04:00
return GraphNode - > FindWidgetForPin ( Pin ) ;
2014-04-25 05:03:13 -04:00
}
}
2014-04-26 09:55:34 -04:00
return TSharedPtr < SGraphPin > ( ) ;
}
2014-04-25 05:03:13 -04:00
2014-03-14 14:13:41 -04:00
/**
* Construct a widget
*
* @ param InArgs The declaration describing how the widgets should be constructed .
*/
void SGraphPanel : : Construct ( const SGraphPanel : : FArguments & InArgs )
{
SNodePanel : : Construct ( ) ;
this - > OnGetContextMenuFor = InArgs . _OnGetContextMenuFor ;
this - > GraphObj = InArgs . _GraphObj ;
this - > GraphObjToDiff = InArgs . _GraphObjToDiff ;
this - > SelectionManager . OnSelectionChanged = InArgs . _OnSelectionChanged ;
this - > IsEditable = InArgs . _IsEditable ;
this - > OnNodeDoubleClicked = InArgs . _OnNodeDoubleClicked ;
this - > OnDropActor = InArgs . _OnDropActor ;
this - > OnDropStreamingLevel = InArgs . _OnDropStreamingLevel ;
this - > OnVerifyTextCommit = InArgs . _OnVerifyTextCommit ;
this - > OnTextCommitted = InArgs . _OnTextCommitted ;
this - > OnSpawnNodeByShortcut = InArgs . _OnSpawnNodeByShortcut ;
this - > OnUpdateGraphPanel = InArgs . _OnUpdateGraphPanel ;
this - > OnDisallowedPinConnection = InArgs . _OnDisallowedPinConnection ;
this - > bPreservePinPreviewConnection = false ;
this - > PinVisibility = SGraphEditor : : Pin_Show ;
CachedAllottedGeometryScaledSize = FVector2D ( 160 , 120 ) ;
if ( InArgs . _InitialZoomToFit )
{
ZoomToFit ( /*bOnlySelection=*/ false ) ;
bTeleportInsteadOfScrollingWhenZoomingToFit = true ;
}
BounceCurve . AddCurve ( 0.0f , 1.0f ) ;
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
FEditorDelegates : : BeginPIE . AddRaw ( this , & SGraphPanel : : OnBeginPIE ) ;
FEditorDelegates : : EndPIE . AddRaw ( this , & SGraphPanel : : OnEndPIE ) ;
2014-03-14 14:13:41 -04:00
// Register for notifications
MyRegisteredGraphChangedDelegate = FOnGraphChanged : : FDelegate : : CreateSP ( this , & SGraphPanel : : OnGraphChanged ) ;
2015-01-08 09:29:27 -05:00
MyRegisteredGraphChangedDelegateHandle = this - > GraphObj - > AddOnGraphChangedHandler ( MyRegisteredGraphChangedDelegate ) ;
2014-06-09 11:12:17 -04:00
ShowGraphStateOverlay = InArgs . _ShowGraphStateOverlay ;
2014-03-14 14:13:41 -04:00
}
SGraphPanel : : ~ SGraphPanel ( )
{
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
FEditorDelegates : : BeginPIE . RemoveAll ( this ) ;
FEditorDelegates : : EndPIE . RemoveAll ( this ) ;
2015-01-08 09:29:27 -05:00
this - > GraphObj - > RemoveOnGraphChangedHandler ( MyRegisteredGraphChangedDelegateHandle ) ;
2014-03-14 14:13:41 -04:00
}
//////////////////////////////////////////////////////////////////////////
2014-07-23 08:23:21 -04:00
int32 SGraphPanel : : OnPaint ( const FPaintArgs & Args , const FGeometry & AllottedGeometry , const FSlateRect & MyClippingRect , FSlateWindowElementList & OutDrawElements , int32 LayerId , const FWidgetStyle & InWidgetStyle , bool bParentEnabled ) const
2014-03-14 14:13:41 -04:00
{
# if SLATE_HD_STATS
SCOPE_CYCLE_COUNTER ( STAT_SlateOnPaint_SGraphPanel ) ;
# endif
CachedAllottedGeometryScaledSize = AllottedGeometry . Size * AllottedGeometry . Scale ;
//Style used for objects that are the same between revisions
FWidgetStyle FadedStyle = InWidgetStyle ;
FadedStyle . BlendColorAndOpacityTint ( FLinearColor ( 0.45f , 0.45f , 0.45f , 0.45f ) ) ;
// First paint the background
const UEditorExperimentalSettings & Options = * GetDefault < UEditorExperimentalSettings > ( ) ;
const FSlateBrush * BackgroundImage = FEditorStyle : : GetBrush ( TEXT ( " Graph.Panel.SolidBackground " ) ) ;
PaintBackgroundAsLines ( BackgroundImage , AllottedGeometry , MyClippingRect , OutDrawElements , LayerId ) ;
const float ZoomFactor = AllottedGeometry . Scale * GetZoomAmount ( ) ;
FArrangedChildren ArrangedChildren ( EVisibility : : Visible ) ;
2014-07-22 04:03:40 -04:00
ArrangeChildNodes ( AllottedGeometry , ArrangedChildren ) ;
2014-03-14 14:13:41 -04:00
// Determine some 'global' settings based on current LOD
const bool bDrawShadowsThisFrame = GetCurrentLOD ( ) > EGraphRenderingLOD : : LowestDetail ;
// Because we paint multiple children, we must track the maximum layer id that they produced in case one of our parents
// wants to an overlay for all of its contents.
// Save LayerId for comment boxes to ensure they always appear below nodes & wires
const int32 CommentNodeShadowLayerId = LayerId + + ;
const int32 CommentNodeLayerId = LayerId + + ;
// Save a LayerId for wires, which appear below nodes but above comments
// We will draw them later, along with the arrows which appear above nodes.
const int32 WireLayerId = LayerId + + ;
const int32 NodeShadowsLayerId = LayerId ;
const int32 NodeLayerId = NodeShadowsLayerId + 1 ;
int32 MaxLayerId = NodeLayerId ;
2014-09-15 21:48:46 -04:00
const FVector2D NodeShadowSize = GetDefault < UGraphEditorSettings > ( ) - > GetShadowDeltaSize ( ) ;
2014-03-14 14:13:41 -04:00
const UEdGraphSchema * Schema = GraphObj - > GetSchema ( ) ;
// Draw the child nodes
{
// When drawing a marquee, need a preview of what the selection will be.
const FGraphPanelSelectionSet * SelectionToVisualize = & ( SelectionManager . SelectedNodes ) ;
FGraphPanelSelectionSet SelectionPreview ;
if ( Marquee . IsValid ( ) )
{
ApplyMarqueeSelection ( Marquee , SelectionManager . SelectedNodes , SelectionPreview ) ;
SelectionToVisualize = & SelectionPreview ;
}
// Context for rendering node infos
FKismetNodeInfoContext Context ( GraphObj ) ;
TArray < FGraphDiffControl : : FNodeMatch > NodeMatches ;
for ( int32 ChildIndex = 0 ; ChildIndex < ArrangedChildren . Num ( ) ; + + ChildIndex )
{
2014-08-25 12:51:49 -04:00
FArrangedWidget & CurWidget = ArrangedChildren [ ChildIndex ] ;
2014-03-14 14:13:41 -04:00
TSharedRef < SGraphNode > ChildNode = StaticCastSharedRef < SGraphNode > ( CurWidget . Widget ) ;
// Examine node to see what layers we should be drawing in
int32 ShadowLayerId = NodeShadowsLayerId ;
int32 ChildLayerId = NodeLayerId ;
// If a comment node, draw in the dedicated comment slots
{
UObject * NodeObj = ChildNode - > GetObjectBeingDisplayed ( ) ;
if ( NodeObj & & NodeObj - > IsA ( UEdGraphNode_Comment : : StaticClass ( ) ) )
{
ShadowLayerId = CommentNodeShadowLayerId ;
ChildLayerId = CommentNodeLayerId ;
}
}
const bool bNodeIsVisible = FSlateRect : : DoRectanglesIntersect ( CurWidget . Geometry . GetClippingRect ( ) , MyClippingRect ) ;
if ( bNodeIsVisible )
{
const bool bSelected = SelectionToVisualize - > Contains ( StaticCastSharedRef < SNodePanel : : SNode > ( CurWidget . Widget ) - > GetObjectBeingDisplayed ( ) ) ;
// Handle Node renaming once the node is visible
if ( bSelected & & ChildNode - > IsRenamePending ( ) )
{
ChildNode - > ApplyRename ( ) ;
}
// Draw the node's shadow.
if ( bDrawShadowsThisFrame | | bSelected )
{
const FSlateBrush * ShadowBrush = ChildNode - > GetShadowBrush ( bSelected ) ;
FSlateDrawElement : : MakeBox (
OutDrawElements ,
ShadowLayerId ,
CurWidget . Geometry . ToInflatedPaintGeometry ( NodeShadowSize ) ,
ShadowBrush ,
MyClippingRect
) ;
}
// Draw the comments and information popups for this node, if it has any.
{
float CommentBubbleY = 0.0f ;
Context . bSelected = bSelected ;
TArray < FGraphInformationPopupInfo > Popups ;
{
ChildNode - > GetNodeInfoPopups ( & Context , /*out*/ Popups ) ;
}
for ( int32 PopupIndex = 0 ; PopupIndex < Popups . Num ( ) ; + + PopupIndex )
{
FGraphInformationPopupInfo & Popup = Popups [ PopupIndex ] ;
PaintComment ( Popup . Message , CurWidget . Geometry , MyClippingRect , OutDrawElements , ChildLayerId , Popup . BackgroundColor , /*inout*/ CommentBubbleY , InWidgetStyle ) ;
}
}
int32 CurWidgetsMaxLayerId ;
{
UEdGraphNode * NodeObj = Cast < UEdGraphNode > ( ChildNode - > GetObjectBeingDisplayed ( ) ) ;
/** When diffing nodes, nodes that are different between revisions are opaque, nodes that have not changed are faded */
FGraphDiffControl : : FNodeMatch NodeMatch = FGraphDiffControl : : FindNodeMatch ( GraphObjToDiff , NodeObj , NodeMatches ) ;
if ( NodeMatch . IsValid ( ) )
{
NodeMatches . Add ( NodeMatch ) ;
}
const bool bNodeIsDifferent = ( ! GraphObjToDiff | | NodeMatch . Diff ( ) ) ;
/* When dragging off a pin, we want to duck the alpha of some nodes */
2014-04-25 05:03:13 -04:00
TSharedPtr < SGraphPin > OnlyStartPin = ( 1 = = PreviewConnectorFromPins . Num ( ) ) ? PreviewConnectorFromPins [ 0 ] . FindInGraphPanel ( * this ) : TSharedPtr < SGraphPin > ( ) ;
2014-03-14 14:13:41 -04:00
const bool bNodeIsNotUsableInCurrentContext = Schema - > FadeNodeWhenDraggingOffPin ( NodeObj , OnlyStartPin . IsValid ( ) ? OnlyStartPin . Get ( ) - > GetPinObj ( ) : NULL ) ;
const FWidgetStyle & NodeStyleToUse = ( bNodeIsDifferent & & ! bNodeIsNotUsableInCurrentContext ) ? InWidgetStyle : FadedStyle ;
// Draw the node.O
2014-07-23 08:23:21 -04:00
CurWidgetsMaxLayerId = CurWidget . Widget - > Paint ( Args . WithNewParent ( this ) , CurWidget . Geometry , MyClippingRect , OutDrawElements , ChildLayerId , NodeStyleToUse , ShouldBeEnabled ( bParentEnabled ) ) ;
2014-03-14 14:13:41 -04:00
}
// Draw the node's overlay, if it has one.
{
// Get its size
const FVector2D WidgetSize = CurWidget . Geometry . Size ;
{
2014-07-22 04:03:40 -04:00
TArray < FOverlayBrushInfo > OverlayBrushes ;
ChildNode - > GetOverlayBrushes ( bSelected , WidgetSize , /*out*/ OverlayBrushes ) ;
for ( int32 BrushIndex = 0 ; BrushIndex < OverlayBrushes . Num ( ) ; + + BrushIndex )
2014-03-14 14:13:41 -04:00
{
2014-07-22 04:03:40 -04:00
FOverlayBrushInfo & OverlayInfo = OverlayBrushes [ BrushIndex ] ;
const FSlateBrush * OverlayBrush = OverlayInfo . Brush ;
if ( OverlayBrush ! = NULL )
{
FPaintGeometry BouncedGeometry = CurWidget . Geometry . ToPaintGeometry ( OverlayInfo . OverlayOffset , OverlayBrush - > ImageSize , 1.f ) ;
2014-03-14 14:13:41 -04:00
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
// Handle bouncing during PIE
const float BounceValue = FMath : : Sin ( 2.0f * PI * BounceCurve . GetLerp ( ) ) ;
2014-07-22 04:03:40 -04:00
BouncedGeometry . DrawPosition + = ( OverlayInfo . AnimationEnvelope * BounceValue * ZoomFactor ) ;
CurWidgetsMaxLayerId + + ;
FSlateDrawElement : : MakeBox (
OutDrawElements ,
CurWidgetsMaxLayerId ,
BouncedGeometry ,
OverlayBrush ,
MyClippingRect
) ;
}
2014-03-14 14:13:41 -04:00
}
2014-07-22 04:03:40 -04:00
}
2014-03-14 14:13:41 -04:00
2014-07-22 04:03:40 -04:00
{
TArray < FOverlayWidgetInfo > OverlayWidgets = ChildNode - > GetOverlayWidgets ( bSelected , WidgetSize ) ;
for ( int32 WidgetIndex = 0 ; WidgetIndex < OverlayWidgets . Num ( ) ; + + WidgetIndex )
{
FOverlayWidgetInfo & OverlayInfo = OverlayWidgets [ WidgetIndex ] ;
if ( OverlayInfo . Widget - > GetVisibility ( ) = = EVisibility : : Visible )
{
// call SlatePrepass as these widgets are not in the 'normal' child hierarchy
OverlayInfo . Widget - > SlatePrepass ( ) ;
const FGeometry WidgetGeometry = CurWidget . Geometry . MakeChild ( OverlayInfo . OverlayOffset , OverlayInfo . Widget - > GetDesiredSize ( ) , 1.f ) ;
2014-07-23 08:23:21 -04:00
OverlayInfo . Widget - > Paint ( Args . WithNewParent ( this ) , WidgetGeometry , MyClippingRect , OutDrawElements , CurWidgetsMaxLayerId , InWidgetStyle , bParentEnabled ) ;
2014-07-22 04:03:40 -04:00
}
}
2014-03-14 14:13:41 -04:00
}
}
MaxLayerId = FMath : : Max ( MaxLayerId , CurWidgetsMaxLayerId + 1 ) ;
}
}
}
MaxLayerId + = 1 ;
// Draw connections between pins
if ( Children . Num ( ) > 0 )
{
//@TODO: Pull this into a factory like the pin and node ones
FConnectionDrawingPolicy * ConnectionDrawingPolicy ;
{
ConnectionDrawingPolicy = Schema - > CreateConnectionDrawingPolicy ( WireLayerId , MaxLayerId , ZoomFactor , MyClippingRect , OutDrawElements , GraphObj ) ;
if ( ! ConnectionDrawingPolicy )
{
if ( Schema - > IsA ( UAnimationGraphSchema : : StaticClass ( ) ) )
{
ConnectionDrawingPolicy = new FAnimGraphConnectionDrawingPolicy ( WireLayerId , MaxLayerId , ZoomFactor , MyClippingRect , OutDrawElements , GraphObj ) ;
}
else if ( Schema - > IsA ( UAnimationStateMachineSchema : : StaticClass ( ) ) )
{
ConnectionDrawingPolicy = new FStateMachineConnectionDrawingPolicy ( WireLayerId , MaxLayerId , ZoomFactor , MyClippingRect , OutDrawElements , GraphObj ) ;
}
else if ( Schema - > IsA ( UEdGraphSchema_K2 : : StaticClass ( ) ) )
{
ConnectionDrawingPolicy = new FKismetConnectionDrawingPolicy ( WireLayerId , MaxLayerId , ZoomFactor , MyClippingRect , OutDrawElements , GraphObj ) ;
}
else if ( Schema - > IsA ( USoundCueGraphSchema : : StaticClass ( ) ) )
{
ConnectionDrawingPolicy = new FSoundCueGraphConnectionDrawingPolicy ( WireLayerId , MaxLayerId , ZoomFactor , MyClippingRect , OutDrawElements , GraphObj ) ;
}
else if ( Schema - > IsA ( UMaterialGraphSchema : : StaticClass ( ) ) )
{
ConnectionDrawingPolicy = new FMaterialGraphConnectionDrawingPolicy ( WireLayerId , MaxLayerId , ZoomFactor , MyClippingRect , OutDrawElements , GraphObj ) ;
}
else
{
ConnectionDrawingPolicy = new FConnectionDrawingPolicy ( WireLayerId , MaxLayerId , ZoomFactor , MyClippingRect , OutDrawElements ) ;
}
}
}
2014-04-25 05:03:13 -04:00
TArray < TSharedPtr < SGraphPin > > OverridePins ;
for ( const FGraphPinHandle & Handle : PreviewConnectorFromPins )
{
TSharedPtr < SGraphPin > Pin = Handle . FindInGraphPanel ( * this ) ;
if ( Pin . IsValid ( ) )
{
OverridePins . Add ( Pin ) ;
}
}
ConnectionDrawingPolicy - > SetHoveredPins ( CurrentHoveredPins , OverridePins , TimeSinceMouseEnteredPin ) ;
2014-03-14 14:13:41 -04:00
ConnectionDrawingPolicy - > SetMarkedPin ( MarkedPin ) ;
// Get the set of pins for all children and synthesize geometry for culled out pins so lines can be drawn to them.
TMap < TSharedRef < SWidget > , FArrangedWidget > PinGeometries ;
TSet < TSharedRef < SWidget > > VisiblePins ;
for ( int32 ChildIndex = 0 ; ChildIndex < Children . Num ( ) ; + + ChildIndex )
{
TSharedRef < SGraphNode > ChildNode = StaticCastSharedRef < SGraphNode > ( Children [ ChildIndex ] ) ;
// If this is a culled node, approximate the pin geometry to the corner of the node it is within
if ( IsNodeCulled ( ChildNode , AllottedGeometry ) )
{
TArray < TSharedRef < SWidget > > NodePins ;
ChildNode - > GetPins ( NodePins ) ;
const FVector2D NodeLoc = ChildNode - > GetPosition ( ) ;
const FGeometry SynthesizedNodeGeometry ( GraphCoordToPanelCoord ( NodeLoc ) , AllottedGeometry . AbsolutePosition , FVector2D : : ZeroVector , 1.f ) ;
for ( TArray < TSharedRef < SWidget > > : : TConstIterator NodePinIterator ( NodePins ) ; NodePinIterator ; + + NodePinIterator )
{
const SGraphPin & PinWidget = static_cast < const SGraphPin & > ( ( * NodePinIterator ) . Get ( ) ) ;
FVector2D PinLoc = NodeLoc + PinWidget . GetNodeOffset ( ) ;
const FGeometry SynthesizedPinGeometry ( GraphCoordToPanelCoord ( PinLoc ) , AllottedGeometry . AbsolutePosition , FVector2D : : ZeroVector , 1.f ) ;
PinGeometries . Add ( * NodePinIterator , FArrangedWidget ( * NodePinIterator , SynthesizedPinGeometry ) ) ;
}
// Also add synthesized geometries for culled nodes
ArrangedChildren . AddWidget ( FArrangedWidget ( ChildNode , SynthesizedNodeGeometry ) ) ;
}
else
{
ChildNode - > GetPins ( VisiblePins ) ;
}
}
// Now get the pin geometry for all visible children and append it to the PinGeometries map
TMap < TSharedRef < SWidget > , FArrangedWidget > VisiblePinGeometries ;
{
this - > FindChildGeometries ( AllottedGeometry , VisiblePins , VisiblePinGeometries ) ;
PinGeometries . Append ( VisiblePinGeometries ) ;
}
// Draw preview connections (only connected on one end)
if ( PreviewConnectorFromPins . Num ( ) > 0 )
{
2014-04-25 05:03:13 -04:00
for ( const FGraphPinHandle & Handle : PreviewConnectorFromPins )
2014-03-14 14:13:41 -04:00
{
2014-04-25 05:03:13 -04:00
TSharedPtr < SGraphPin > CurrentStartPin = Handle . FindInGraphPanel ( * this ) ;
if ( ! CurrentStartPin . IsValid ( ) )
{
continue ;
}
2014-03-14 14:13:41 -04:00
const FArrangedWidget * PinGeometry = PinGeometries . Find ( CurrentStartPin . ToSharedRef ( ) ) ;
if ( PinGeometry ! = NULL )
{
FVector2D StartPoint ;
FVector2D EndPoint ;
if ( CurrentStartPin - > GetDirection ( ) = = EGPD_Input )
{
StartPoint = AllottedGeometry . AbsolutePosition + PreviewConnectorEndpoint ;
EndPoint = FGeometryHelper : : VerticalMiddleLeftOf ( PinGeometry - > Geometry ) - FVector2D ( ConnectionDrawingPolicy - > ArrowRadius . X , 0 ) ;
}
else
{
StartPoint = FGeometryHelper : : VerticalMiddleRightOf ( PinGeometry - > Geometry ) ;
EndPoint = AllottedGeometry . AbsolutePosition + PreviewConnectorEndpoint ;
}
ConnectionDrawingPolicy - > DrawPreviewConnector ( PinGeometry - > Geometry , StartPoint , EndPoint , CurrentStartPin . Get ( ) - > GetPinObj ( ) ) ;
}
//@TODO: Re-evaluate this incompatible mojo; it's mutating every pin state every frame to accomplish a visual effect
ConnectionDrawingPolicy - > SetIncompatiblePinDrawState ( CurrentStartPin , VisiblePins ) ;
}
}
else
{
//@TODO: Re-evaluate this incompatible mojo; it's mutating every pin state every frame to accomplish a visual effect
ConnectionDrawingPolicy - > ResetIncompatiblePinDrawState ( VisiblePins ) ;
}
// Draw all regular connections
ConnectionDrawingPolicy - > Draw ( PinGeometries , ArrangedChildren ) ;
delete ConnectionDrawingPolicy ;
}
// Draw a shadow overlay around the edges of the graph
+ + MaxLayerId ;
PaintSurroundSunkenShadow ( FEditorStyle : : GetBrush ( TEXT ( " Graph.Shadow " ) ) , AllottedGeometry , MyClippingRect , OutDrawElements , MaxLayerId ) ;
2014-06-09 11:12:17 -04:00
if ( ShowGraphStateOverlay . Get ( ) )
2014-03-14 14:13:41 -04:00
{
2014-06-09 11:12:17 -04:00
const FSlateBrush * BorderBrush = nullptr ;
if ( ( GEditor - > bIsSimulatingInEditor | | GEditor - > PlayWorld ! = NULL ) )
{
// Draw a surrounding indicator when PIE is active, to make it clear that the graph is read-only, etc...
BorderBrush = FEditorStyle : : GetBrush ( TEXT ( " Graph.PlayInEditor " ) ) ;
}
else if ( ! IsEditable . Get ( ) )
{
// Draw a different border when we're not simulating but the graph is read-only
BorderBrush = FEditorStyle : : GetBrush ( TEXT ( " Graph.ReadOnlyBorder " ) ) ;
}
2014-05-01 05:32:30 -04:00
2014-06-09 11:12:17 -04:00
if ( BorderBrush )
{
// Actually draw the border
FSlateDrawElement : : MakeBox (
OutDrawElements ,
MaxLayerId ,
AllottedGeometry . ToPaintGeometry ( ) ,
BorderBrush ,
MyClippingRect
) ;
}
2014-03-14 14:13:41 -04:00
}
// Draw the marquee selection rectangle
PaintMarquee ( AllottedGeometry , MyClippingRect , OutDrawElements , MaxLayerId ) ;
// Draw the software cursor
+ + MaxLayerId ;
PaintSoftwareCursor ( AllottedGeometry , MyClippingRect , OutDrawElements , MaxLayerId ) ;
return MaxLayerId ;
}
bool SGraphPanel : : SupportsKeyboardFocus ( ) const
{
return true ;
}
2014-07-22 04:03:40 -04:00
void SGraphPanel : : OnArrangeChildren ( const FGeometry & AllottedGeometry , FArrangedChildren & ArrangedChildren ) const
{
SNodePanel : : OnArrangeChildren ( AllottedGeometry , ArrangedChildren ) ;
FArrangedChildren MyArrangedChildren ( ArrangedChildren . GetFilter ( ) ) ;
for ( int32 ChildIndex = 0 ; ChildIndex < ArrangedChildren . Num ( ) ; + + ChildIndex )
{
2014-08-25 12:51:49 -04:00
FArrangedWidget & CurWidget = ArrangedChildren [ ChildIndex ] ;
2014-07-22 04:03:40 -04:00
TSharedRef < SGraphNode > ChildNode = StaticCastSharedRef < SGraphNode > ( CurWidget . Widget ) ;
TArray < FOverlayWidgetInfo > OverlayWidgets = ChildNode - > GetOverlayWidgets ( false , CurWidget . Geometry . Size ) ;
for ( int32 WidgetIndex = 0 ; WidgetIndex < OverlayWidgets . Num ( ) ; + + WidgetIndex )
{
FOverlayWidgetInfo & OverlayInfo = OverlayWidgets [ WidgetIndex ] ;
MyArrangedChildren . AddWidget ( AllottedGeometry . MakeChild ( OverlayInfo . Widget . ToSharedRef ( ) , CurWidget . Geometry . Position + OverlayInfo . OverlayOffset , OverlayInfo . Widget - > GetDesiredSize ( ) , GetZoomAmount ( ) ) ) ;
}
}
ArrangedChildren . Append ( MyArrangedChildren ) ;
}
2014-03-14 14:13:41 -04:00
void SGraphPanel : : UpdateSelectedNodesPositions ( FVector2D PositionIncrement )
{
for ( FGraphPanelSelectionSet : : TIterator NodeIt ( SelectionManager . SelectedNodes ) ; NodeIt ; + + NodeIt )
{
TSharedRef < SNode > * pWidget = NodeToWidgetLookup . Find ( * NodeIt ) ;
if ( pWidget ! = NULL )
{
SNode & Widget = pWidget - > Get ( ) ;
2014-05-06 04:45:44 -04:00
SNode : : FNodeSet NodeFilter ;
Widget . MoveTo ( Widget . GetPosition ( ) + PositionIncrement , NodeFilter ) ;
2014-03-14 14:13:41 -04:00
}
}
}
2014-10-30 12:29:36 -04:00
FReply SGraphPanel : : OnKeyDown ( const FGeometry & MyGeometry , const FKeyEvent & InKeyEvent )
2014-03-14 14:13:41 -04:00
{
if ( IsEditable . Get ( ) )
{
2014-10-30 12:29:36 -04:00
if ( InKeyEvent . GetKey ( ) = = EKeys : : Up | | InKeyEvent . GetKey ( ) = = EKeys : : NumPadEight )
2014-03-14 14:13:41 -04:00
{
UpdateSelectedNodesPositions ( FVector2D ( 0.0f , - GetSnapGridSize ( ) ) ) ;
return FReply : : Handled ( ) ;
}
2014-10-30 12:29:36 -04:00
if ( InKeyEvent . GetKey ( ) = = EKeys : : Down | | InKeyEvent . GetKey ( ) = = EKeys : : NumPadTwo )
2014-03-14 14:13:41 -04:00
{
UpdateSelectedNodesPositions ( FVector2D ( 0.0f , GetSnapGridSize ( ) ) ) ;
return FReply : : Handled ( ) ;
}
2014-10-30 12:29:36 -04:00
if ( InKeyEvent . GetKey ( ) = = EKeys : : Right | | InKeyEvent . GetKey ( ) = = EKeys : : NumPadSix )
2014-03-14 14:13:41 -04:00
{
UpdateSelectedNodesPositions ( FVector2D ( GetSnapGridSize ( ) , 0.0f ) ) ;
return FReply : : Handled ( ) ;
}
2014-10-30 12:29:36 -04:00
if ( InKeyEvent . GetKey ( ) = = EKeys : : Left | | InKeyEvent . GetKey ( ) = = EKeys : : NumPadFour )
2014-03-14 14:13:41 -04:00
{
UpdateSelectedNodesPositions ( FVector2D ( - GetSnapGridSize ( ) , 0.0f ) ) ;
return FReply : : Handled ( ) ;
}
2014-10-30 12:29:36 -04:00
if ( InKeyEvent . GetKey ( ) = = EKeys : : Subtract )
2014-03-14 14:13:41 -04:00
{
2014-10-30 12:29:36 -04:00
ChangeZoomLevel ( - 1 , CachedAllottedGeometryScaledSize / 2.f , InKeyEvent . IsControlDown ( ) ) ;
2014-03-14 14:13:41 -04:00
return FReply : : Handled ( ) ;
}
2014-10-30 12:29:36 -04:00
if ( InKeyEvent . GetKey ( ) = = EKeys : : Add )
2014-03-14 14:13:41 -04:00
{
2014-10-30 12:29:36 -04:00
ChangeZoomLevel ( + 1 , CachedAllottedGeometryScaledSize / 2.f , InKeyEvent . IsControlDown ( ) ) ;
2014-03-14 14:13:41 -04:00
return FReply : : Handled ( ) ;
}
}
2014-10-30 12:29:36 -04:00
return SNodePanel : : OnKeyDown ( MyGeometry , InKeyEvent ) ;
2014-03-14 14:13:41 -04:00
}
void SGraphPanel : : GetAllPins ( TSet < TSharedRef < SWidget > > & AllPins )
{
// Get the set of pins for all children
for ( int32 ChildIndex = 0 ; ChildIndex < Children . Num ( ) ; + + ChildIndex )
{
TSharedRef < SGraphNode > ChildNode = StaticCastSharedRef < SGraphNode > ( Children [ ChildIndex ] ) ;
ChildNode - > GetPins ( AllPins ) ;
}
}
void SGraphPanel : : AddPinToHoverSet ( UEdGraphPin * HoveredPin )
{
CurrentHoveredPins . Add ( HoveredPin ) ;
TimeSinceMouseEnteredPin = FSlateApplication : : Get ( ) . GetCurrentTime ( ) ;
}
void SGraphPanel : : RemovePinFromHoverSet ( UEdGraphPin * UnhoveredPin )
{
CurrentHoveredPins . Remove ( UnhoveredPin ) ;
TimeSinceMouseLeftPin = FSlateApplication : : Get ( ) . GetCurrentTime ( ) ;
}
void SGraphPanel : : ArrangeChildrenForContextMenuSummon ( const FGeometry & AllottedGeometry , FArrangedChildren & ArrangedChildren ) const
{
// First pass nodes
for ( int32 ChildIndex = 0 ; ChildIndex < VisibleChildren . Num ( ) ; + + ChildIndex )
{
const TSharedRef < SNode > & SomeChild = VisibleChildren [ ChildIndex ] ;
if ( ! SomeChild - > RequiresSecondPassLayout ( ) )
{
ArrangedChildren . AddWidget ( AllottedGeometry . MakeChild ( SomeChild , SomeChild - > GetPosition ( ) - ViewOffset , SomeChild - > GetDesiredSizeForMarquee ( ) , GetZoomAmount ( ) ) ) ;
}
}
// Second pass nodes
for ( int32 ChildIndex = 0 ; ChildIndex < VisibleChildren . Num ( ) ; + + ChildIndex )
{
const TSharedRef < SNode > & SomeChild = VisibleChildren [ ChildIndex ] ;
if ( SomeChild - > RequiresSecondPassLayout ( ) )
{
SomeChild - > PerformSecondPassLayout ( NodeToWidgetLookup ) ;
ArrangedChildren . AddWidget ( AllottedGeometry . MakeChild ( SomeChild , SomeChild - > GetPosition ( ) - ViewOffset , SomeChild - > GetDesiredSizeForMarquee ( ) , GetZoomAmount ( ) ) ) ;
}
}
}
TSharedPtr < SWidget > SGraphPanel : : OnSummonContextMenu ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent )
{
//Editability is up to the user to consider for menu options
{
// If we didn't drag very far, summon a context menu.
// Figure out what's under the mouse: Node, Pin or just the Panel, and summon the context menu for that.
UEdGraphNode * NodeUnderCursor = NULL ;
UEdGraphPin * PinUnderCursor = NULL ;
{
FArrangedChildren ArrangedNodes ( EVisibility : : Visible ) ;
this - > ArrangeChildrenForContextMenuSummon ( MyGeometry , ArrangedNodes ) ;
const int32 HoveredNodeIndex = SWidget : : FindChildUnderMouse ( ArrangedNodes , MouseEvent ) ;
if ( HoveredNodeIndex ! = INDEX_NONE )
{
2014-08-25 12:51:49 -04:00
const FArrangedWidget & HoveredNode = ArrangedNodes [ HoveredNodeIndex ] ;
2014-09-11 07:02:40 -04:00
TSharedRef < SGraphNode > GraphNode = StaticCastSharedRef < SGraphNode > ( HoveredNode . Widget ) ;
TSharedPtr < SGraphNode > GraphSubNode = GraphNode - > GetNodeUnderMouse ( HoveredNode . Geometry , MouseEvent ) ;
GraphNode = GraphSubNode . IsValid ( ) ? GraphSubNode . ToSharedRef ( ) : GraphNode ;
2014-03-14 14:13:41 -04:00
NodeUnderCursor = GraphNode - > GetNodeObj ( ) ;
// Selection should switch to this code if it isn't already selected.
// When multiple nodes are selected, we do nothing, provided that the
// node for which the context menu is being created is in the selection set.
if ( ! SelectionManager . IsNodeSelected ( GraphNode - > GetObjectBeingDisplayed ( ) ) )
{
SelectionManager . SelectSingleNode ( GraphNode - > GetObjectBeingDisplayed ( ) ) ;
}
const TSharedPtr < SGraphPin > HoveredPin = GraphNode - > GetHoveredPin ( HoveredNode . Geometry , MouseEvent ) ;
if ( HoveredPin . IsValid ( ) )
{
PinUnderCursor = HoveredPin - > GetPinObj ( ) ;
}
}
}
const FVector2D NodeAddPosition = PanelCoordToGraphCoord ( MyGeometry . AbsoluteToLocal ( MouseEvent . GetScreenSpacePosition ( ) ) ) ;
TArray < UEdGraphPin * > NoSourcePins ;
2014-05-15 17:34:14 -04:00
return SummonContextMenu ( MouseEvent . GetScreenSpacePosition ( ) , NodeAddPosition , NodeUnderCursor , PinUnderCursor , NoSourcePins , MouseEvent . IsShiftDown ( ) ) ;
2014-03-14 14:13:41 -04:00
}
return TSharedPtr < SWidget > ( ) ;
}
bool SGraphPanel : : OnHandleLeftMouseRelease ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent )
{
2014-04-25 05:03:13 -04:00
TSharedPtr < SGraphPin > PreviewConnectionPin = PreviewConnectorFromPins . Num ( ) > 0 ? PreviewConnectorFromPins [ 0 ] . FindInGraphPanel ( * this ) : nullptr ;
if ( PreviewConnectionPin . IsValid ( ) & & IsEditable . Get ( ) )
2014-03-14 14:13:41 -04:00
{
TSet < TSharedRef < SWidget > > AllConnectors ;
for ( int32 ChildIndex = 0 ; ChildIndex < Children . Num ( ) ; + + ChildIndex )
{
//@FINDME:
TSharedRef < SGraphNode > ChildNode = StaticCastSharedRef < SGraphNode > ( Children [ ChildIndex ] ) ;
ChildNode - > GetPins ( AllConnectors ) ;
}
TMap < TSharedRef < SWidget > , FArrangedWidget > PinGeometries ;
this - > FindChildGeometries ( MyGeometry , AllConnectors , PinGeometries ) ;
bool bHandledDrop = false ;
TSet < UEdGraphNode * > NodeList ;
for ( TMap < TSharedRef < SWidget > , FArrangedWidget > : : TIterator SomePinIt ( PinGeometries ) ; ! bHandledDrop & & SomePinIt ; + + SomePinIt )
{
FArrangedWidget & PinWidgetGeometry = SomePinIt . Value ( ) ;
if ( PinWidgetGeometry . Geometry . IsUnderLocation ( MouseEvent . GetScreenSpacePosition ( ) ) )
{
SGraphPin & TargetPin = static_cast < SGraphPin & > ( PinWidgetGeometry . Widget . Get ( ) ) ;
2014-04-25 05:03:13 -04:00
if ( PreviewConnectionPin - > TryHandlePinConnection ( TargetPin ) )
2014-03-14 14:13:41 -04:00
{
NodeList . Add ( TargetPin . GetPinObj ( ) - > GetOwningNode ( ) ) ;
2014-04-25 05:03:13 -04:00
NodeList . Add ( PreviewConnectionPin - > GetPinObj ( ) - > GetOwningNode ( ) ) ;
2014-03-14 14:13:41 -04:00
}
bHandledDrop = true ;
}
}
// No longer make a connection for a pin; we just connected or failed to connect.
OnStopMakingConnection ( /*bForceStop=*/ true ) ;
return true ;
}
else
{
return false ;
}
}
void SGraphPanel : : OnDragEnter ( const FGeometry & MyGeometry , const FDragDropEvent & DragDropEvent )
{
2014-04-23 18:00:50 -04:00
TSharedPtr < FGraphEditorDragDropAction > DragConnectionOp = DragDropEvent . GetOperationAs < FGraphEditorDragDropAction > ( ) ;
if ( DragConnectionOp . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
DragConnectionOp - > SetHoveredGraph ( SharedThis ( this ) ) ;
}
}
void SGraphPanel : : OnDragLeave ( const FDragDropEvent & DragDropEvent )
{
2014-06-18 05:04:59 -04:00
TSharedPtr < FGraphEditorDragDropAction > Operation = DragDropEvent . GetOperationAs < FGraphEditorDragDropAction > ( ) ;
if ( Operation . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
2014-06-18 05:04:59 -04:00
Operation - > SetHoveredGraph ( TSharedPtr < SGraphPanel > ( NULL ) ) ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 18:00:50 -04:00
else
2014-03-14 14:13:41 -04:00
{
2014-06-18 05:04:59 -04:00
TSharedPtr < FDecoratedDragDropOp > AssetOp = DragDropEvent . GetOperationAs < FDecoratedDragDropOp > ( ) ;
if ( AssetOp . IsValid ( ) )
2014-04-23 18:00:50 -04:00
{
AssetOp - > ResetToDefaultToolTip ( ) ;
}
2014-03-14 14:13:41 -04:00
}
}
FReply SGraphPanel : : OnDragOver ( const FGeometry & MyGeometry , const FDragDropEvent & DragDropEvent )
{
2014-04-23 18:00:50 -04:00
TSharedPtr < FDragDropOperation > Operation = DragDropEvent . GetOperation ( ) ;
if ( ! Operation . IsValid ( ) )
{
return FReply : : Unhandled ( ) ;
}
2014-06-18 05:04:59 -04:00
// Handle Read only graphs
if ( ! IsEditable . Get ( ) )
{
TSharedPtr < FGraphEditorDragDropAction > GraphDragDropOp = DragDropEvent . GetOperationAs < FGraphEditorDragDropAction > ( ) ;
if ( GraphDragDropOp . IsValid ( ) )
{
GraphDragDropOp - > SetDropTargetValid ( false ) ;
}
else
{
TSharedPtr < FDecoratedDragDropOp > AssetOp = DragDropEvent . GetOperationAs < FDecoratedDragDropOp > ( ) ;
if ( AssetOp . IsValid ( ) )
{
FText Tooltip = AssetOp - > GetHoverText ( ) ;
if ( Tooltip . IsEmpty ( ) )
{
Tooltip = NSLOCTEXT ( " GraphPanel " , " DragDropOperation " , " Graph is Read-Only " ) ;
}
AssetOp - > SetToolTip ( Tooltip , FEditorStyle : : GetBrush ( TEXT ( " Graph.ConnectorFeedback.Error " ) ) ) ;
}
}
return FReply : : Handled ( ) ;
}
2014-04-23 18:00:50 -04:00
if ( Operation - > IsOfType < FGraphEditorDragDropAction > ( ) )
2014-03-14 14:13:41 -04:00
{
PreviewConnectorEndpoint = MyGeometry . AbsoluteToLocal ( DragDropEvent . GetScreenSpacePosition ( ) ) ;
return FReply : : Handled ( ) ;
}
2014-04-23 18:00:50 -04:00
else if ( Operation - > IsOfType < FExternalDragOperation > ( ) )
2014-03-14 14:13:41 -04:00
{
return AssetUtil : : CanHandleAssetDrag ( DragDropEvent ) ;
}
2014-04-23 18:00:50 -04:00
else if ( Operation - > IsOfType < FAssetDragDropOp > ( ) )
2014-03-14 14:13:41 -04:00
{
if ( GraphObj ! = NULL & & GraphObj - > GetSchema ( ) )
{
2014-04-23 18:00:50 -04:00
TSharedPtr < FAssetDragDropOp > AssetOp = StaticCastSharedPtr < FAssetDragDropOp > ( Operation ) ;
2014-03-14 14:13:41 -04:00
bool bOkIcon = false ;
FString TooltipText ;
GraphObj - > GetSchema ( ) - > GetAssetsGraphHoverMessage ( AssetOp - > AssetData , GraphObj , TooltipText , bOkIcon ) ;
const FSlateBrush * TooltipIcon = bOkIcon ? FEditorStyle : : GetBrush ( TEXT ( " Graph.ConnectorFeedback.OK " ) ) : FEditorStyle : : GetBrush ( TEXT ( " Graph.ConnectorFeedback.Error " ) ) ; ;
2014-04-23 17:55:20 -04:00
AssetOp - > SetToolTip ( FText : : FromString ( TooltipText ) , TooltipIcon ) ;
2014-03-14 14:13:41 -04:00
}
return FReply : : Handled ( ) ;
}
else
{
return FReply : : Unhandled ( ) ;
}
}
FReply SGraphPanel : : OnDrop ( const FGeometry & MyGeometry , const FDragDropEvent & DragDropEvent )
{
const FVector2D NodeAddPosition = PanelCoordToGraphCoord ( MyGeometry . AbsoluteToLocal ( DragDropEvent . GetScreenSpacePosition ( ) ) ) ;
2014-10-30 12:29:36 -04:00
FSlateApplication : : Get ( ) . SetKeyboardFocus ( AsShared ( ) , EFocusCause : : SetDirectly ) ;
2014-03-14 14:13:41 -04:00
2014-04-23 18:00:50 -04:00
TSharedPtr < FDragDropOperation > Operation = DragDropEvent . GetOperation ( ) ;
2014-06-18 05:04:59 -04:00
if ( ! Operation . IsValid ( ) | | ! IsEditable . Get ( ) )
2014-04-23 18:00:50 -04:00
{
return FReply : : Unhandled ( ) ;
}
if ( Operation - > IsOfType < FGraphEditorDragDropAction > ( ) )
2014-03-14 14:13:41 -04:00
{
check ( GraphObj ) ;
2014-12-01 12:58:44 -05:00
TSharedPtr < FGraphEditorDragDropAction > DragConn = StaticCastSharedPtr < FGraphEditorDragDropAction > ( Operation ) ;
if ( DragConn . IsValid ( ) & & DragConn - > IsSupportedBySchema ( GraphObj - > GetSchema ( ) ) )
2014-11-19 20:45:22 -05:00
{
return DragConn - > DroppedOnPanel ( SharedThis ( this ) , DragDropEvent . GetScreenSpacePosition ( ) , NodeAddPosition , * GraphObj ) ;
}
return FReply : : Unhandled ( ) ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 18:00:50 -04:00
else if ( Operation - > IsOfType < FActorDragDropGraphEdOp > ( ) )
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:00:50 -04:00
TSharedPtr < FActorDragDropGraphEdOp > ActorOp = StaticCastSharedPtr < FActorDragDropGraphEdOp > ( Operation ) ;
2014-03-14 14:13:41 -04:00
OnDropActor . ExecuteIfBound ( ActorOp - > Actors , GraphObj , NodeAddPosition ) ;
return FReply : : Handled ( ) ;
}
2014-04-23 18:00:50 -04:00
else if ( Operation - > IsOfType < FLevelDragDropOp > ( ) )
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:00:50 -04:00
TSharedPtr < FLevelDragDropOp > LevelOp = StaticCastSharedPtr < FLevelDragDropOp > ( Operation ) ;
2014-03-14 14:13:41 -04:00
OnDropStreamingLevel . ExecuteIfBound ( LevelOp - > StreamingLevelsToDrop , GraphObj , NodeAddPosition ) ;
return FReply : : Handled ( ) ;
}
else
{
if ( GraphObj ! = NULL & & GraphObj - > GetSchema ( ) ! = NULL )
{
TArray < FAssetData > DroppedAssetData = AssetUtil : : ExtractAssetDataFromDrag ( DragDropEvent ) ;
if ( DroppedAssetData . Num ( ) > 0 )
{
GraphObj - > GetSchema ( ) - > DroppedAssetsOnGraph ( DroppedAssetData , NodeAddPosition , GraphObj ) ;
return FReply : : Handled ( ) ;
}
}
return FReply : : Unhandled ( ) ;
}
}
2014-05-15 17:34:14 -04:00
void SGraphPanel : : OnBeginMakingConnection ( UEdGraphPin * InOriginatingPin )
2014-03-14 14:13:41 -04:00
{
2014-05-15 17:34:14 -04:00
if ( InOriginatingPin ! = nullptr )
2014-04-25 05:03:13 -04:00
{
2014-05-15 17:34:14 -04:00
PreviewConnectorFromPins . Add ( InOriginatingPin ) ;
2014-04-25 05:03:13 -04:00
}
2014-03-14 14:13:41 -04:00
}
void SGraphPanel : : OnStopMakingConnection ( bool bForceStop )
{
if ( bForceStop | | ! bPreservePinPreviewConnection )
{
PreviewConnectorFromPins . Reset ( ) ;
bPreservePinPreviewConnection = false ;
}
}
void SGraphPanel : : PreservePinPreviewUntilForced ( )
{
bPreservePinPreviewConnection = true ;
}
/** Add a slot to the CanvasPanel dynamically */
void SGraphPanel : : AddGraphNode ( const TSharedRef < SNodePanel : : SNode > & NodeToAdd )
{
TSharedRef < SGraphNode > GraphNode = StaticCastSharedRef < SGraphNode > ( NodeToAdd ) ;
GraphNode - > SetOwner ( SharedThis ( this ) ) ;
const UEdGraphNode * Node = GraphNode - > GetNodeObj ( ) ;
2014-04-25 05:03:13 -04:00
if ( Node )
{
NodeGuidMap . Add ( Node - > NodeGuid , GraphNode ) ;
}
2014-12-05 05:33:22 -05:00
SNodePanel : : AddGraphNode ( NodeToAdd ) ;
2014-03-14 14:13:41 -04:00
}
void SGraphPanel : : RemoveAllNodes ( )
{
2014-04-25 05:03:13 -04:00
NodeGuidMap . Empty ( ) ;
2014-03-14 14:13:41 -04:00
CurrentHoveredPins . Empty ( ) ;
SNodePanel : : RemoveAllNodes ( ) ;
}
2014-05-15 17:34:14 -04:00
TSharedPtr < SWidget > SGraphPanel : : SummonContextMenu ( const FVector2D & WhereToSummon , const FVector2D & WhereToAddNode , UEdGraphNode * ForNode , UEdGraphPin * ForPin , const TArray < UEdGraphPin * > & DragFromPins , bool bShiftOperation )
2014-03-14 14:13:41 -04:00
{
if ( OnGetContextMenuFor . IsBound ( ) )
{
2014-05-15 17:34:14 -04:00
FGraphContextMenuArguments SpawnInfo ;
SpawnInfo . NodeAddPosition = WhereToAddNode ;
SpawnInfo . GraphNode = ForNode ;
SpawnInfo . GraphPin = ForPin ;
SpawnInfo . DragFromPins = DragFromPins ;
SpawnInfo . bShiftOperation = bShiftOperation ;
FActionMenuContent FocusedContent = OnGetContextMenuFor . Execute ( SpawnInfo ) ;
2014-03-14 14:13:41 -04:00
TSharedRef < SWidget > MenuContent =
SNew ( SBorder )
. BorderImage ( FEditorStyle : : GetBrush ( " Menu.Background " ) )
[
FocusedContent . Content
] ;
FSlateApplication : : Get ( ) . PushMenu (
AsShared ( ) ,
MenuContent ,
WhereToSummon ,
FPopupTransitionEffect ( FPopupTransitionEffect : : ContextMenu )
) ;
return FocusedContent . WidgetToFocus ;
}
return TSharedPtr < SWidget > ( ) ;
}
void SGraphPanel : : AttachGraphEvents ( TSharedPtr < SGraphNode > CreatedSubNode )
{
check ( CreatedSubNode . IsValid ( ) ) ;
CreatedSubNode - > SetIsEditable ( IsEditable ) ;
CreatedSubNode - > SetDoubleClickEvent ( OnNodeDoubleClicked ) ;
CreatedSubNode - > SetVerifyTextCommitEvent ( OnVerifyTextCommit ) ;
CreatedSubNode - > SetTextCommittedEvent ( OnTextCommitted ) ;
}
2014-12-02 09:07:38 -05:00
const TSharedRef < SGraphNode > SGraphPanel : : GetChild ( int32 ChildIndex )
{
return StaticCastSharedRef < SGraphNode > ( Children [ ChildIndex ] ) ;
}
2014-05-15 17:34:14 -04:00
void SGraphPanel : : AddNode ( UEdGraphNode * Node )
2014-03-14 14:13:41 -04:00
{
TSharedPtr < SGraphNode > NewNode = FNodeFactory : : CreateNodeWidget ( Node ) ;
check ( NewNode . IsValid ( ) ) ;
2014-09-30 14:13:40 -04:00
const bool bWasUserAdded = ( UserAddedNodes . Find ( Node ) ! = nullptr ) ;
2014-03-14 14:13:41 -04:00
NewNode - > SetIsEditable ( IsEditable ) ;
NewNode - > SetDoubleClickEvent ( OnNodeDoubleClicked ) ;
NewNode - > SetVerifyTextCommitEvent ( OnVerifyTextCommit ) ;
NewNode - > SetTextCommittedEvent ( OnTextCommitted ) ;
NewNode - > SetDisallowedPinConnectionEvent ( OnDisallowedPinConnection ) ;
this - > AddGraphNode
(
NewNode . ToSharedRef ( )
) ;
if ( bWasUserAdded )
{
// Add the node to visible children, this allows focus to occur on sub-widgets for naming purposes.
VisibleChildren . Add ( NewNode . ToSharedRef ( ) ) ;
NewNode - > PlaySpawnEffect ( ) ;
2014-05-15 17:34:14 -04:00
NewNode - > RequestRenameOnSpawn ( ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-04-25 05:03:13 -04:00
TSharedPtr < SGraphNode > SGraphPanel : : GetNodeWidgetFromGuid ( FGuid Guid ) const
{
return NodeGuidMap . FindRef ( Guid ) . Pin ( ) ;
}
2014-03-14 14:13:41 -04:00
void SGraphPanel : : Update ( )
{
// Add widgets for all the nodes that don't have one.
if ( GraphObj ! = NULL )
{
// Scan for all missing nodes
2014-09-30 14:13:40 -04:00
for ( int32 NodeIndex = 0 ; NodeIndex < GraphObj - > Nodes . Num ( ) ; + + NodeIndex )
2014-03-14 14:13:41 -04:00
{
UEdGraphNode * Node = GraphObj - > Nodes [ NodeIndex ] ;
if ( Node )
{
AddNode ( Node ) ;
}
else
{
UE_LOG ( LogGraphPanel , Warning , TEXT ( " Found NULL Node in GraphObj array. A node type has been deleted without creating an ActiveClassRedictor to K2Node_DeadClass. " ) ) ;
}
}
2014-09-30 14:13:40 -04:00
// find the last selection action, and execute it
for ( int32 ActionIndex = UserActions . Num ( ) - 1 ; ActionIndex > = 0 ; - - ActionIndex )
{
if ( UserActions [ ActionIndex ] . Action & GRAPHACTION_SelectNode )
{
DeferredSelectionTargetObjects . Empty ( ) ;
for ( const UEdGraphNode * Node : UserActions [ ActionIndex ] . Nodes )
{
DeferredSelectionTargetObjects . Add ( Node ) ;
}
break ;
}
}
2014-03-14 14:13:41 -04:00
}
else
{
RemoveAllNodes ( ) ;
}
// Clean out set of added nodes
UserAddedNodes . Empty ( ) ;
2014-09-30 14:13:40 -04:00
UserActions . Empty ( ) ;
2014-03-14 14:13:41 -04:00
// Invoke any delegate methods
OnUpdateGraphPanel . ExecuteIfBound ( ) ;
}
// Purges the existing visual representation (typically followed by an Update call in the next tick)
void SGraphPanel : : PurgeVisualRepresentation ( )
{
RemoveAllNodes ( ) ;
}
bool SGraphPanel : : IsNodeTitleVisible ( const class UEdGraphNode * Node , bool bRequestRename )
{
bool bTitleVisible = false ;
TSharedRef < SNode > * pWidget = NodeToWidgetLookup . Find ( Node ) ;
if ( pWidget ! = NULL )
{
TWeakPtr < SGraphNode > GraphNode = StaticCastSharedRef < SGraphNode > ( * pWidget ) ;
if ( GraphNode . IsValid ( ) & & ! HasMouseCapture ( ) )
{
FSlateRect TitleRect = GraphNode . Pin ( ) - > GetTitleRect ( ) ;
const FVector2D TopLeft = FVector2D ( TitleRect . Left , TitleRect . Top ) ;
const FVector2D BottomRight = FVector2D ( TitleRect . Right , TitleRect . Bottom ) ;
if ( IsRectVisible ( TopLeft , BottomRight ) )
{
bTitleVisible = true ;
}
else if ( bRequestRename )
{
bTitleVisible = JumpToRect ( TopLeft , BottomRight ) ;
}
if ( bTitleVisible & & bRequestRename )
{
GraphNode . Pin ( ) - > RequestRename ( ) ;
}
}
}
return bTitleVisible ;
}
bool SGraphPanel : : IsRectVisible ( const FVector2D & TopLeft , const FVector2D & BottomRight )
{
return TopLeft > = PanelCoordToGraphCoord ( FVector2D : : ZeroVector ) & & BottomRight < = PanelCoordToGraphCoord ( CachedAllottedGeometryScaledSize ) ;
}
bool SGraphPanel : : JumpToRect ( const FVector2D & TopLeft , const FVector2D & BottomRight )
{
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
ZoomToTarget ( TopLeft , BottomRight ) ;
2014-03-14 14:13:41 -04:00
return true ;
}
void SGraphPanel : : JumpToNode ( const UEdGraphNode * JumpToMe , bool bRequestRename )
{
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
if ( JumpToMe ! = nullptr )
2014-03-14 14:13:41 -04:00
{
if ( bRequestRename )
{
TSharedRef < SNode > * pWidget = NodeToWidgetLookup . Find ( JumpToMe ) ;
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
if ( pWidget ! = nullptr )
2014-03-14 14:13:41 -04:00
{
TSharedRef < SGraphNode > GraphNode = StaticCastSharedRef < SGraphNode > ( * pWidget ) ;
GraphNode - > RequestRename ( ) ;
}
}
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
2014-03-14 14:13:41 -04:00
// Select this node, and request that we jump to it.
SelectAndCenterObject ( JumpToMe , true ) ;
}
}
void SGraphPanel : : JumpToPin ( const UEdGraphPin * JumpToMe )
{
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
if ( JumpToMe ! = nullptr )
2014-03-14 14:13:41 -04:00
{
JumpToNode ( JumpToMe - > GetOwningNode ( ) , false ) ;
}
}
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
void SGraphPanel : : OnBeginPIE ( const bool bIsSimulating )
{
// Play the bounce curve on a continuous loop during PIE
BounceCurve . Play ( this - > AsShared ( ) , true ) ;
}
void SGraphPanel : : OnEndPIE ( const bool bIsSimulating )
{
// Stop the bounce curve
BounceCurve . JumpToEnd ( ) ;
}
2014-09-30 14:13:40 -04:00
void SGraphPanel : : OnGraphChanged ( const FEdGraphEditAction & EditAction )
2014-03-14 14:13:41 -04:00
{
2014-09-30 14:13:40 -04:00
if ( ( EditAction . Graph = = GraphObj ) & &
( EditAction . Nodes . Num ( ) > 0 ) & &
2014-03-14 14:13:41 -04:00
// We do not want to mark it as a UserAddedNode for graphs that do not currently have focus,
// this causes each one to want to do the effects and rename, which causes problems.
2014-09-30 14:13:40 -04:00
( HasKeyboardFocus ( ) | | HasFocusedDescendants ( ) ) )
2014-03-14 14:13:41 -04:00
{
2014-09-30 14:13:40 -04:00
int32 ActionIndex = UserActions . Num ( ) ;
if ( EditAction . Action & GRAPHACTION_AddNode )
{
for ( const UEdGraphNode * Node : EditAction . Nodes )
{
UserAddedNodes . Add ( Node , ActionIndex ) ;
}
}
UserActions . Add ( EditAction ) ;
2014-03-14 14:13:41 -04:00
}
}
void SGraphPanel : : NotifyGraphChanged ( const FEdGraphEditAction & EditAction )
{
// Forward call
OnGraphChanged ( EditAction ) ;
}
void SGraphPanel : : AddReferencedObjects ( FReferenceCollector & Collector )
{
Collector . AddReferencedObject ( GraphObj ) ;
Collector . AddReferencedObject ( GraphObjToDiff ) ;
2014-12-05 05:33:22 -05:00
}