2019-12-26 15:33:43 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# pragma once
2016-11-23 15:48:37 -05:00
# include "CoreMinimal.h"
2022-06-10 14:52:59 -04:00
# include "DiffResults.h"
2016-11-23 15:48:37 -05:00
# include "Misc/Attribute.h"
2018-03-27 14:27:07 -04:00
# include "Misc/Guid.h"
2016-11-23 15:48:37 -05:00
# include "Layout/Visibility.h"
# include "Layout/SlateRect.h"
# include "Layout/Geometry.h"
# include "Input/CursorReply.h"
# include "Input/Reply.h"
# include "Styling/SlateColor.h"
# include "Layout/ArrangedWidget.h"
# include "Layout/Margin.h"
# include "Animation/CurveSequence.h"
# include "SlotBase.h"
# include "Layout/Children.h"
# include "Widgets/SPanel.h"
# include "Styling/CoreStyle.h"
# include "Framework/Commands/InputChord.h"
# include "GraphEditor.h"
# include "Layout/ArrangedChildren.h"
# include "Types/PaintArgs.h"
2022-05-09 13:12:28 -04:00
# include "Styling/AppStyle.h"
2016-11-23 15:48:37 -05:00
# include "Layout/LayoutUtils.h"
2014-10-14 22:50:06 -04:00
# include "MarqueeOperation.h"
2018-01-20 11:19:29 -05:00
# include "Templates/UniquePtr.h"
2020-09-24 00:43:27 -04:00
# include "UObject/GCObject.h"
2014-03-14 14:13:41 -04:00
2016-11-23 15:48:37 -05:00
class FActiveTimerHandle ;
class FScopedTransaction ;
class FSlateWindowElementList ;
struct FMarqueeOperation ;
struct Rect ;
2014-03-14 14:13:41 -04:00
//@TODO: Too generic of a name to expose at this scope
typedef class UObject * SelectedItemType ;
// Level of detail for graph rendering (lower numbers are 'further away' with fewer details)
namespace EGraphRenderingLOD
{
enum Type
{
// Detail level when zoomed all the way out (all performance optimizations enabled)
LowestDetail ,
// Detail level that text starts being disabled because it is unreadable
LowDetail ,
// Detail level at which text starts to get hard to read but is still drawn
MediumDetail ,
// Detail level when zoomed in at 1:1
DefaultDetail ,
// Detail level when fully zoomed in (past 1:1)
FullyZoomedIn ,
} ;
}
// Context passed in when getting popup info
struct FNodeInfoContext
{
public :
bool bSelected ;
} ;
// Entry for an overlay brush in the node panel
struct FOverlayBrushInfo
{
public :
/** Brush to draw */
const FSlateBrush * Brush ;
/** Scale of animation to apply */
FVector2D AnimationEnvelope ;
/** Offset origin of the overlay from the widget */
FVector2D OverlayOffset ;
public :
FOverlayBrushInfo ( )
: Brush ( NULL )
, AnimationEnvelope ( 0.0f , 0.0f )
, OverlayOffset ( 0.f , 0.f )
{
}
FOverlayBrushInfo ( const FSlateBrush * InBrush )
: Brush ( InBrush )
, AnimationEnvelope ( 0.0f , 0.0f )
, OverlayOffset ( 0.f , 0.f )
{
}
FOverlayBrushInfo ( const FSlateBrush * InBrush , float HorizontalBounce )
: Brush ( InBrush )
, AnimationEnvelope ( HorizontalBounce , 0.0f )
, OverlayOffset ( 0.f , 0.f )
{
}
} ;
2014-07-22 04:03:40 -04:00
// Entry for an overlay widget in the node panel
struct FOverlayWidgetInfo
{
public :
/** Widget to use */
TSharedPtr < SWidget > Widget ;
/** Offset origin of the overlay from the widget */
FVector2D OverlayOffset ;
public :
FOverlayWidgetInfo ( )
: Widget ( nullptr )
, OverlayOffset ( 0.f , 0.f )
{
}
FOverlayWidgetInfo ( TSharedPtr < SWidget > InWidget )
: Widget ( InWidget )
, OverlayOffset ( 0.f , 0.f )
{
}
} ;
2014-03-14 14:13:41 -04:00
// Entry for an information popup in the node panel
struct FGraphInformationPopupInfo
{
public :
const FSlateBrush * Icon ;
FLinearColor BackgroundColor ;
FString Message ;
public :
FGraphInformationPopupInfo ( const FSlateBrush * InIcon , FLinearColor InBackgroundColor , const FString & InMessage )
: Icon ( InIcon )
, BackgroundColor ( InBackgroundColor )
, Message ( InMessage )
{
}
} ;
/**
* Interface for ZoomLevel values
* Provides mapping for a range of virtual ZoomLevel values to actual node scaling values
*/
struct FZoomLevelsContainer
{
/**
* @param InZoomLevel virtual zoom level value
*
* @return associated scaling value
*/
virtual float GetZoomAmount ( int32 InZoomLevel ) const = 0 ;
/**
* @param InZoomAmount scaling value
*
* @return nearest ZoomLevel mapping for provided scale value
*/
virtual int32 GetNearestZoomLevel ( float InZoomAmount ) const = 0 ;
/**
* @param InZoomLevel virtual zoom level value
*
* @return associated friendly name
*/
virtual FText GetZoomText ( int32 InZoomLevel ) const = 0 ;
/**
* @return count of supported zoom levels
*/
virtual int32 GetNumZoomLevels ( ) const = 0 ;
/**
* @return the optimal(1:1) zoom level value, default zoom level for the graph
*/
virtual int32 GetDefaultZoomLevel ( ) const = 0 ;
/**
* @param InZoomLevel virtual zoom level value
*
* @return associated LOD value
*/
virtual EGraphRenderingLOD : : Type GetLOD ( int32 InZoomLevel ) const = 0 ;
// Necessary for Mac OS X to compile 'delete <pointer_to_this_object>;'
virtual ~ FZoomLevelsContainer ( void ) { } ;
} ;
2020-09-24 00:43:27 -04:00
struct GRAPHEDITOR_API FGraphSelectionManager : public FGCObject
2014-10-14 22:50:06 -04:00
{
FGraphPanelSelectionSet SelectedNodes ;
/** Invoked when the selected graph nodes have changed. */
SGraphEditor : : FOnSelectionChanged OnSelectionChanged ;
public :
/** @return the set of selected nodes */
const FGraphPanelSelectionSet & GetSelectedNodes ( ) const ;
/** Select just the specified node */
void SelectSingleNode ( SelectedItemType Node ) ;
/** Reset the selection state of all nodes */
void ClearSelectionSet ( ) ;
/** Returns true if any nodes are selected */
bool AreAnyNodesSelected ( ) const
{
return SelectedNodes . Num ( ) > 0 ;
}
/** Changes the selection set to contain exactly all of the passed in nodes */
void SetSelectionSet ( FGraphPanelSelectionSet & NewSet ) ;
/**
* Add or remove a node from the selection set
*
* @param Node Node the affect.
* @param bSelect true to select the node; false to unselect.
*/
void SetNodeSelection ( SelectedItemType Node , bool bSelect ) ;
/** @return true if Node is selected; false otherwise */
bool IsNodeSelected ( SelectedItemType Node ) const ;
// Handle the selection mechanics of starting to drag a node
void StartDraggingNode ( SelectedItemType NodeBeingDragged , const FPointerEvent & MouseEvent ) ;
// Handle the selection mechanics when a node is clicked on
void ClickedOnNode ( SelectedItemType Node , const FPointerEvent & MouseEvent ) ;
2020-09-24 00:43:27 -04:00
void AddReferencedObjects ( FReferenceCollector & Collector ) override ;
2021-04-29 19:32:06 -04:00
virtual FString GetReferencerName ( ) const override
{
return TEXT ( " FGraphSelectionManager " ) ;
}
2014-10-14 22:50:06 -04:00
} ;
2014-03-14 14:13:41 -04:00
/**
* This class is designed to serve as the base class for a panel/canvas that contains interactive widgets
* which can be selected and moved around by the user. It also manages zooming and panning, allowing a larger
* virtual space to be used for the widget placement.
*
* The user is responsible for creating widgets (which must be derived from SNode) and any custom drawing
* code desired. The other main restriction is that each SNode instance must have a unique UObject* associated
* with it.
*/
2014-11-03 10:40:57 -05:00
namespace ENodeZone
{
enum Type
{
TopLeft ,
TopCenter ,
TopRight ,
Left ,
Center ,
Right ,
BottomLeft ,
BottomCenter ,
BottomRight ,
Count
} ;
}
2014-03-14 14:13:41 -04:00
class GRAPHEDITOR_API SNodePanel : public SPanel
{
public :
2014-11-03 10:40:57 -05:00
class SNode : public SPanel
2014-03-14 14:13:41 -04:00
{
public :
2014-05-06 04:45:44 -04:00
2014-11-03 10:40:57 -05:00
/** A slot that support alignment of content and padding and z-order */
2021-06-14 07:35:51 -04:00
class GRAPHEDITOR_API FNodeSlot : public TSlotBase < FNodeSlot > , public TAlignmentWidgetSlotMixin < FNodeSlot >
2014-11-03 10:40:57 -05:00
{
public :
2021-06-11 07:54:18 -04:00
friend SNode ;
2014-11-03 10:40:57 -05:00
FNodeSlot ( )
2021-06-11 07:54:18 -04:00
: FNodeSlot ( ENodeZone : : TopLeft )
2014-11-03 10:40:57 -05:00
{ }
2021-06-11 07:54:18 -04:00
FNodeSlot ( ENodeZone : : Type InZone )
: TSlotBase < FNodeSlot > ( )
, TAlignmentWidgetSlotMixin < FNodeSlot > ( HAlign_Fill , VAlign_Fill )
, Zone ( InZone )
, SlotPadding ( 0.0f )
, Offset ( FVector2D : : ZeroVector )
, AllowScale ( true )
{ }
SLATE_SLOT_BEGIN_ARGS_OneMixin ( FNodeSlot , TSlotBase < FNodeSlot > , TAlignmentWidgetSlotMixin < FNodeSlot > )
SLATE_ATTRIBUTE ( FMargin , Padding )
SLATE_ATTRIBUTE ( FVector2D , SlotOffset )
SLATE_ATTRIBUTE ( FVector2D , SlotSize )
SLATE_ATTRIBUTE ( bool , AllowScaling )
SLATE_SLOT_END_ARGS ( )
2021-06-14 07:35:51 -04:00
void Construct ( const FChildren & SlotOwner , FSlotArguments & & InArgs ) ;
2021-06-11 07:54:18 -04:00
public :
UE_DEPRECATED ( 5.0 , " Padding is now deprecated. Use the FSlotArgument or the SetPadding function. " )
2014-11-03 10:40:57 -05:00
FNodeSlot & Padding ( const TAttribute < FMargin > InPadding )
{
SlotPadding = InPadding ;
return * this ;
}
2021-06-11 07:54:18 -04:00
UE_DEPRECATED ( 5.0 , " SlotOffset is now deprecated. Use the FSlotArgument or the SetSlotOffset function. " )
2014-11-03 10:40:57 -05:00
FNodeSlot & SlotOffset ( const TAttribute < FVector2D > InOffset )
{
Offset = InOffset ;
return * this ;
}
2021-06-11 07:54:18 -04:00
UE_DEPRECATED ( 5.0 , " SlotSize is now deprecated. Use the FSlotArgument or the SetSlotSize function. " )
2014-11-03 10:40:57 -05:00
FNodeSlot & SlotSize ( const TAttribute < FVector2D > InSize )
{
Size = InSize ;
return * this ;
}
2021-06-11 07:54:18 -04:00
UE_DEPRECATED ( 5.0 , " AllowScaling is now deprecated. Use the FSlotArgument or the SetAllowScalingfunction. " )
2014-11-03 10:40:57 -05:00
FNodeSlot & AllowScaling ( const TAttribute < bool > InAllowScale )
{
AllowScale = InAllowScale ;
return * this ;
}
public :
2021-06-11 07:54:18 -04:00
ENodeZone : : Type GetZoneType ( ) const
{
return Zone ;
}
2014-11-03 10:40:57 -05:00
2021-06-11 07:54:18 -04:00
void SetPadding ( TAttribute < FMargin > InPadding )
{
SlotPadding = MoveTemp ( InPadding ) ;
}
FMargin GetPadding ( ) const
{
return SlotPadding . Get ( ) ;
}
void SetSlotOffset ( TAttribute < FVector2D > InOffset )
{
Offset = MoveTemp ( InOffset ) ;
}
FVector2D GetSlotOffset ( ) const
{
return Offset . Get ( ) ;
}
void SetSlotSize ( TAttribute < FVector2D > InSize )
{
Size = MoveTemp ( InSize ) ;
}
FVector2D GetSlotSize ( ) const
{
return Size . Get ( ) ;
}
void SetAllowScaling ( TAttribute < bool > InAllowScaling )
{
AllowScale = MoveTemp ( InAllowScaling ) ;
}
bool GetAllowScaling ( ) const
{
return AllowScale . Get ( ) ;
}
private :
2014-11-03 10:40:57 -05:00
/** The child widget contained in this slot. */
ENodeZone : : Type Zone ;
TAttribute < FMargin > SlotPadding ;
TAttribute < FVector2D > Offset ;
TAttribute < FVector2D > Size ;
TAttribute < bool > AllowScale ;
} ;
2014-05-06 04:45:44 -04:00
typedef TSet < TWeakPtr < SNodePanel : : SNode > > FNodeSet ;
2014-11-03 10:40:57 -05:00
// SPanel Interface
virtual FChildren * GetChildren ( ) override
{
return & Children ;
}
2015-01-14 19:04:45 -05:00
virtual FVector2D ComputeDesiredSize ( float ) const override
2014-11-03 10:40:57 -05:00
{
for ( int32 ChildIndex = 0 ; ChildIndex < Children . Num ( ) ; + + ChildIndex )
{
if ( Children [ ChildIndex ] . Zone = = ENodeZone : : Center )
{
const FNodeSlot & CenterZone = Children [ ChildIndex ] ;
const EVisibility ChildVisibility = CenterZone . GetWidget ( ) - > GetVisibility ( ) ;
if ( ChildVisibility ! = EVisibility : : Collapsed )
{
return ( CenterZone . GetWidget ( ) - > GetDesiredSize ( ) + CenterZone . SlotPadding . Get ( ) . GetDesiredSize ( ) ) * DesiredSizeScale . Get ( ) ;
}
}
}
return FVector2D : : ZeroVector ;
}
2020-02-07 09:51:26 -05:00
virtual float GetRelativeLayoutScale ( int32 ChildIndex , float LayoutScaleMultiplier ) const override
2015-01-14 19:04:45 -05:00
{
2020-02-07 09:51:26 -05:00
const FNodeSlot & ThisSlot = Children [ ChildIndex ] ;
if ( ! ThisSlot . AllowScale . Get ( ) )
2015-01-14 19:04:45 -05:00
{
// Child slots that do not allow zooming should scale themselves to negate the node panel's zoom.
TSharedPtr < SNodePanel > ParentPanel = GetParentPanel ( ) ;
if ( ParentPanel . IsValid ( ) )
2020-02-07 09:51:26 -05:00
{
return 1.0f / ParentPanel - > GetZoomAmount ( ) ;
2015-01-14 19:04:45 -05:00
}
}
return 1.0f ;
}
2014-11-03 10:40:57 -05:00
virtual void OnArrangeChildren ( const FGeometry & AllottedGeometry , FArrangedChildren & ArrangedChildren ) const override
{
for ( int32 ChildIndex = 0 ; ChildIndex < Children . Num ( ) ; + + ChildIndex )
{
const FNodeSlot & CurChild = Children [ ChildIndex ] ;
const EVisibility ChildVisibility = CurChild . GetWidget ( ) - > GetVisibility ( ) ;
if ( ArrangedChildren . Accepts ( ChildVisibility ) )
{
const FMargin SlotPadding ( CurChild . SlotPadding . Get ( ) ) ;
2015-01-14 19:04:45 -05:00
// If this child is not allowed to scale, its scale relative to its parent should undo the parent widget's scaling.
2014-11-03 10:40:57 -05:00
FVector2D Size ;
2014-11-05 13:10:16 -05:00
if ( CurChild . Size . IsSet ( ) )
2014-11-03 10:40:57 -05:00
{
Size = CurChild . Size . Get ( ) ;
}
else
{
2017-06-19 20:27:30 -04:00
AlignmentArrangeResult XResult = AlignChild < Orient_Horizontal > ( AllottedGeometry . GetLocalSize ( ) . X , CurChild , SlotPadding ) ;
AlignmentArrangeResult YResult = AlignChild < Orient_Vertical > ( AllottedGeometry . GetLocalSize ( ) . Y , CurChild , SlotPadding ) ;
2014-11-03 10:40:57 -05:00
Size = FVector2D ( XResult . Size , YResult . Size ) ;
}
2015-01-14 19:04:45 -05:00
const FArrangedWidget ChildGeom =
AllottedGeometry . MakeChild (
2014-11-03 10:40:57 -05:00
CurChild . GetWidget ( ) ,
CurChild . Offset . Get ( ) ,
Size ,
2020-02-07 09:51:26 -05:00
GetRelativeLayoutScale ( ChildIndex , AllottedGeometry . Scale )
2014-11-03 10:40:57 -05:00
) ;
ArrangedChildren . AddWidget ( ChildVisibility , ChildGeom ) ;
}
}
}
2017-06-19 20:27:30 -04:00
virtual int32 OnPaint ( const FPaintArgs & Args , const FGeometry & AllottedGeometry , const FSlateRect & MyCullingRect , FSlateWindowElementList & OutDrawElements , int32 LayerId , const FWidgetStyle & InWidgetStyle , bool bParentEnabled ) const override
2014-11-03 10:40:57 -05:00
{
FArrangedChildren ArrangedChildren ( EVisibility : : Visible ) ;
{
ArrangeChildren ( AllottedGeometry , ArrangedChildren ) ;
}
int32 MaxLayerId = LayerId ;
for ( int32 ChildIndex = 0 ; ChildIndex < ArrangedChildren . Num ( ) ; + + ChildIndex )
{
const FArrangedWidget & CurWidget = ArrangedChildren [ ChildIndex ] ;
2017-06-19 20:27:30 -04:00
if ( ! IsChildWidgetCulled ( MyCullingRect , CurWidget ) )
2014-11-03 10:40:57 -05:00
{
2017-06-19 20:27:30 -04:00
const int32 CurWidgetsMaxLayerId = CurWidget . Widget - > Paint ( Args . WithNewParent ( this ) , CurWidget . Geometry , MyCullingRect , OutDrawElements , LayerId , InWidgetStyle , ShouldBeEnabled ( bParentEnabled ) ) ;
MaxLayerId = FMath : : Max ( MaxLayerId , CurWidgetsMaxLayerId ) ;
2014-11-03 10:40:57 -05:00
}
2019-07-25 10:51:20 -04:00
else
{
//SlateGI - RemoveContent
}
2014-11-03 10:40:57 -05:00
}
2019-07-25 10:51:20 -04:00
2014-11-03 10:40:57 -05:00
return MaxLayerId ;
}
// End of SPanel Interface
2021-06-11 07:54:18 -04:00
using FScopedWidgetSlotArguments = TPanelChildren < FNodeSlot > : : FScopedWidgetSlotArguments ;
FScopedWidgetSlotArguments GetOrAddSlot ( const ENodeZone : : Type SlotId )
2014-11-03 10:40:57 -05:00
{
// Return existing
2021-06-11 07:54:18 -04:00
int32 InsertIndex = INDEX_NONE ;
2014-11-03 10:40:57 -05:00
for ( int32 ChildIndex = 0 ; ChildIndex < Children . Num ( ) ; + + ChildIndex )
{
if ( Children [ ChildIndex ] . Zone = = SlotId )
{
2021-06-11 07:54:18 -04:00
Children . RemoveAt ( ChildIndex ) ;
InsertIndex = ChildIndex ;
2014-11-03 10:40:57 -05:00
}
}
2021-06-11 07:54:18 -04:00
// Add new
return FScopedWidgetSlotArguments { MakeUnique < FNodeSlot > ( SlotId ) , Children , InsertIndex } ;
2014-11-03 10:40:57 -05:00
}
2015-07-14 17:51:17 -04:00
FNodeSlot * GetSlot ( const ENodeZone : : Type SlotId )
{
FNodeSlot * Result = nullptr ;
// Return existing
for ( int32 ChildIndex = 0 ; ChildIndex < Children . Num ( ) ; + + ChildIndex )
{
if ( Children [ ChildIndex ] . Zone = = SlotId )
{
Result = & Children [ ChildIndex ] ;
break ;
}
}
return Result ;
}
void RemoveSlot ( const ENodeZone : : Type SlotId )
{
for ( int32 ChildIndex = 0 ; ChildIndex < Children . Num ( ) ; + + ChildIndex )
{
if ( Children [ ChildIndex ] . Zone = = SlotId )
{
Children . RemoveAt ( ChildIndex ) ;
break ;
}
}
}
2014-05-06 04:45:44 -04:00
/**
* @param NewPosition The Node should be relocated to this position in the graph panel
* @param NodeFilter Set of nodes to prevent movement on, after moving successfully a node is added to this set.
2021-05-18 15:26:27 -04:00
* @param bMarkDirty If we should mark nodes as dirty on move
2014-05-06 04:45:44 -04:00
*/
2021-05-18 15:26:27 -04:00
virtual void MoveTo ( const FVector2D & NewPosition , FNodeSet & NodeFilter , bool bMarkDirty = true )
2014-03-14 14:13:41 -04:00
{
}
/** @return the Node's position within the graph */
virtual FVector2D GetPosition ( ) const
{
return FVector2D ( 0.0f , 0.0f ) ;
}
/** @return a user-specified comment on this node; the comment gets drawn in a bubble above the node */
virtual FString GetNodeComment ( ) const
{
return FString ( ) ;
}
/** @return The backing object, used as a unique identifier in the selection set, etc... */
virtual UObject * GetObjectBeingDisplayed ( ) const
{
return NULL ;
}
/** @return The brush to use for drawing the shadow for this node */
virtual const FSlateBrush * GetShadowBrush ( bool bSelected ) const
{
2022-05-09 13:12:28 -04:00
return bSelected ? FAppStyle : : GetBrush ( TEXT ( " Graph.Node.ShadowSelected " ) ) : FAppStyle : : GetBrush ( TEXT ( " Graph.Node.Shadow " ) ) ;
2014-03-14 14:13:41 -04:00
}
2022-06-10 14:52:59 -04:00
struct DiffHighlightInfo
{
const FSlateBrush * Brush ;
const FLinearColor Tint ;
} ;
/** @return Collection of brushes layered to outline node with the DiffResult color */
TArray < SNodePanel : : SNode : : DiffHighlightInfo > GetDiffHighlights ( const FDiffSingleResult & DiffResult ) const ;
/** used by GetDiffHighlights to generate outlines for diffed nodes */
virtual void GetDiffHighlightBrushes ( const FSlateBrush * & BackgroundOut , const FSlateBrush * & ForegroundOut ) const
{
BackgroundOut = FAppStyle : : GetBrush ( TEXT ( " Graph.Node.DiffHighlight " ) ) ;
ForegroundOut = FAppStyle : : GetBrush ( TEXT ( " Graph.Node.DiffHighlightShading " ) ) ;
}
2014-03-14 14:13:41 -04:00
/** Populate the brushes array with any overlay brushes to render */
virtual void GetOverlayBrushes ( bool bSelected , const FVector2D WidgetSize , TArray < FOverlayBrushInfo > & Brushes ) const
{
}
2014-07-22 04:03:40 -04:00
/** Populate the widgets array with any overlay widgets to render */
virtual TArray < FOverlayWidgetInfo > GetOverlayWidgets ( bool bSelected , const FVector2D & WidgetSize ) const
{
return TArray < FOverlayWidgetInfo > ( ) ;
}
2014-03-14 14:13:41 -04:00
/** Populate the popups array with any popups to render */
virtual void GetNodeInfoPopups ( FNodeInfoContext * Context , TArray < FGraphInformationPopupInfo > & Popups ) const
{
2014-07-22 04:03:40 -04:00
}
2014-03-14 14:13:41 -04:00
/** Returns true if this node is dependent on the location of other nodes (it can only depend on the location of first-pass only nodes) */
virtual bool RequiresSecondPassLayout ( ) const
{
return false ;
}
/** Performs second pass layout; only called if RequiresSecondPassLayout returned true */
virtual void PerformSecondPassLayout ( const TMap < UObject * , TSharedRef < SNode > > & InNodeToWidgetLookup ) const
{
}
/** Return false if this node should not be culled. Useful for potentially large nodes that may be improperly culled. */
virtual bool ShouldAllowCulling ( ) const
{
return true ;
}
/** return if the node can be selected, by pointing given location */
virtual bool CanBeSelected ( const FVector2D & MousePositionInNode ) const
{
return true ;
}
2015-01-05 08:54:17 -05:00
/** Called when user interaction has completed */
virtual void EndUserInteraction ( ) const { }
2014-03-14 14:13:41 -04:00
/**
* override, when area used to select node, should be different, than it's size
* e.g. comment node - only title bar is selectable
* return size of node used for Marquee selecting
*/
virtual FVector2D GetDesiredSizeForMarquee ( ) const
{
return GetDesiredSize ( ) ;
}
2014-12-05 05:33:22 -05:00
// Returns node sort depth, defaults to and is generally 0 for most nodes
virtual int32 GetSortDepth ( ) const { return 0 ; }
// Node Sort Operator
bool operator < ( const SNodePanel : : SNode & NodeIn ) const
{
return GetSortDepth ( ) < NodeIn . GetSortDepth ( ) ;
}
2015-01-14 19:04:45 -05:00
void SetParentPanel ( const TSharedPtr < SNodePanel > & InParent )
{
ParentPanelPtr = InParent ;
}
2014-03-14 14:13:41 -04:00
protected :
SNode ( )
2014-11-03 10:40:57 -05:00
: BorderImage ( FCoreStyle : : Get ( ) . GetBrush ( " NoBorder " ) )
2022-05-09 13:12:28 -04:00
, BorderBackgroundColor ( FAppStyle : : GetColor ( " Graph.ForegroundColor " ) )
2014-11-03 10:40:57 -05:00
, DesiredSizeScale ( FVector2D ( 1 , 1 ) )
2018-02-22 11:25:06 -05:00
, Children ( this )
2014-03-14 14:13:41 -04:00
{
2020-02-07 09:51:26 -05:00
bHasRelativeLayoutScale = true ;
2014-03-14 14:13:41 -04:00
}
2014-11-03 10:40:57 -05:00
protected :
// SBorder Begin
TAttribute < const FSlateBrush * > BorderImage ;
TAttribute < FSlateColor > BorderBackgroundColor ;
TAttribute < FVector2D > DesiredSizeScale ;
/** Whether or not to show the disabled effect when this border is disabled */
TAttribute < bool > ShowDisabledEffect ;
/** Mouse event handlers */
FPointerEventHandler MouseButtonDownHandler ;
FPointerEventHandler MouseButtonUpHandler ;
FPointerEventHandler MouseMoveHandler ;
FPointerEventHandler MouseDoubleClickHandler ;
// SBorder End
// SPanel Begin
/** The layout scale to apply to this widget's contents; useful for animation. */
TAttribute < FVector2D > ContentScale ;
/** The color and opacity to apply to this widget and all its descendants. */
TAttribute < FLinearColor > ColorAndOpacity ;
/** Optional foreground color that will be inherited by all of this widget's contents */
TAttribute < FSlateColor > ForegroundColor ;
// SPanel End
private :
2015-01-14 19:04:45 -05:00
TSharedPtr < SNodePanel > GetParentPanel ( ) const
{
return ParentPanelPtr . Pin ( ) ;
}
2014-11-03 10:40:57 -05:00
TPanelChildren < FNodeSlot > Children ;
2015-01-14 19:04:45 -05:00
TWeakPtr < SNodePanel > ParentPanelPtr ;
2014-11-03 10:40:57 -05:00
2014-03-14 14:13:41 -04:00
} ;
2014-07-28 06:53:40 -04:00
SNodePanel ( ) ;
2014-03-14 14:13:41 -04:00
// SPanel interface
2014-06-13 06:14:46 -04:00
virtual void OnArrangeChildren ( const FGeometry & AllottedGeometry , FArrangedChildren & ArrangedChildren ) const override ;
2015-01-14 19:04:45 -05:00
virtual FVector2D ComputeDesiredSize ( float ) const override ;
2014-06-13 06:14:46 -04:00
virtual FChildren * GetChildren ( ) override ;
2014-03-14 14:13:41 -04:00
// End of SPanel interface
// SWidget interface
2014-12-17 16:07:57 -05:00
virtual void Tick ( const FGeometry & AllottedGeometry , const double InCurrentTime , const float InDeltaTime ) override ;
2014-06-13 06:14:46 -04:00
virtual FReply OnMouseButtonDown ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent ) override ;
virtual FReply OnMouseButtonUp ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent ) override ;
2021-05-18 15:26:27 -04:00
virtual void OnMouseCaptureLost ( const FCaptureLostEvent & CaptureLostEvent ) override ;
2014-06-13 06:14:46 -04:00
virtual FReply OnMouseMove ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent ) override ;
virtual FReply OnMouseWheel ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent ) override ;
virtual FCursorReply OnCursorQuery ( const FGeometry & MyGeometry , const FPointerEvent & CursorEvent ) const override ;
2014-10-30 12:29:36 -04:00
virtual FReply OnKeyDown ( const FGeometry & MyGeometry , const FKeyEvent & InKeyEvent ) override ;
virtual FReply OnKeyUp ( const FGeometry & MyGeometry , const FKeyEvent & InKeyEvent ) override ;
virtual void OnFocusLost ( const FFocusEvent & InFocusEvent ) override ;
2014-06-13 06:14:46 -04:00
virtual FReply OnTouchGesture ( const FGeometry & MyGeometry , const FPointerEvent & GestureEvent ) override ;
2014-07-08 18:23:55 -04:00
virtual FReply OnTouchEnded ( const FGeometry & MyGeometry , const FPointerEvent & InTouchEvent ) override ;
2020-02-07 09:51:26 -05:00
virtual float GetRelativeLayoutScale ( int32 ChildIndex , float LayoutScaleMultiplier ) const override ;
2014-03-14 14:13:41 -04:00
// End of SWidget interface
public :
/**
* Is the given node being observed by a widget in this panel?
*
* @param Node The node to look for.
*
* @return True if the node is being observed by some widget in this panel; false otherwise.
*/
bool Contains ( UObject * Node ) const ;
/** @retun the zoom amount; e.g. a value of 0.25f results in quarter-sized nodes */
float GetZoomAmount ( ) const ;
/** @return Zoom level as a pretty string */
FText GetZoomText ( ) const ;
FSlateColor GetZoomTextColorAndOpacity ( ) const ;
/** @return the view offset in graph space */
FVector2D GetViewOffset ( ) const ;
2022-05-19 19:12:34 -04:00
/**
* when a panel is scrolling/zooming to a target, this can be called to get it's destination
* @param TopLeft top left corner of the destination
* @param BottomRight bottom right corner of the destination
* @return true if there's a scrolling/zooming target and false if there is no destination
*/
bool GetZoomTargetRect ( FVector2D & TopLeft , FVector2D & BottomRight ) const ;
2018-03-27 14:27:07 -04:00
/** @return the current view bookmark ID */
const FGuid & GetViewBookmarkId ( ) const { return CurrentBookmarkGuid ; }
2014-03-14 14:13:41 -04:00
/** Given a coordinate in panel space (i.e. panel widget space), return the same coordinate in graph space while taking zoom and panning into account */
FVector2D PanelCoordToGraphCoord ( const FVector2D & PanelSpaceCoordinate ) const ;
2018-03-27 14:27:07 -04:00
/** Restore the graph panel to the supplied view offset/zoom. Also, optionally set the current bookmark ID. */
void RestoreViewSettings ( const FVector2D & InViewOffset , float InZoomAmount , const FGuid & InBookmarkGuid = FGuid ( ) ) ;
2014-03-14 14:13:41 -04:00
/** Get the grid snap size */
2016-10-27 20:37:57 -04:00
static float GetSnapGridSize ( ) ;
2014-03-14 14:13:41 -04:00
2014-12-17 16:07:57 -05:00
/**
* Zooms out to fit either all nodes or only the selected ones.
* @param bOnlySelection Whether to zoom to fit around only the current selection (if false, will zoom to the extents of all nodes)
*/
2014-03-14 14:13:41 -04:00
void ZoomToFit ( bool bOnlySelection ) ;
/** Get the bounding area for the currently selected nodes
@return false if nothing is selected */
bool GetBoundsForSelectedNodes ( /*out*/ class FSlateRect & Rect , float Padding = 0.0f ) ;
/** @return the position where where nodes should be pasted (i.e. from the clipboard) */
FVector2D GetPastePosition ( ) const ;
/** Ask panel to scroll to location */
void RequestDeferredPan ( const FVector2D & TargetPosition ) ;
/** If it is focusing on a particular object */
bool HasDeferredObjectFocus ( ) const ;
2022-06-21 19:07:19 -04:00
/** Query whether this graph is about to start panning/zooming towards a destination */
bool HasDeferredZoomDestination ( ) const ;
2021-05-18 15:26:27 -04:00
/** Commit transactions for any node movements */
void FinalizeNodeMovements ( ) ;
2014-03-14 14:13:41 -04:00
/** Returns the current LOD level of this panel, based on the zoom factor */
EGraphRenderingLOD : : Type GetCurrentLOD ( ) const { return CurrentLOD ; }
/** Returns if the panel has been panned or zoomed since the last update */
bool HasMoved ( ) const ;
2014-11-17 14:04:07 -05:00
/** Returns all the panel children rather than only visible */
FChildren * GetAllChildren ( ) ;
2014-03-14 14:13:41 -04:00
protected :
/** Initialize members */
void Construct ( ) ;
2014-12-17 16:07:57 -05:00
/**
* Zooms to the specified target rect
* @param TopLeft The top left corner of the target rect
* @param BottomRight The bottom right corner of the target rect
*/
void ZoomToTarget ( const FVector2D & TopLeft , const FVector2D & BottomRight ) ;
2014-03-14 14:13:41 -04:00
/** Update the new view offset location */
void UpdateViewOffset ( const FGeometry & MyGeometry , const FVector2D & TargetPosition ) ;
/** Compute much panel needs to change to pan to location */
static FVector2D ComputeEdgePanAmount ( const FGeometry & MyGeometry , const FVector2D & MouseEvent ) ;
/** Given a coordinate in graph space (e.g. a node's position), return the same coordinate in widget space while taking zoom and panning into account */
FVector2D GraphCoordToPanelCoord ( const FVector2D & GraphSpaceCoordinate ) const ;
/** Given a rectangle in panel space, return a rectangle in graph space. */
FSlateRect PanelRectToGraphRect ( const FSlateRect & PanelSpaceRect ) const ;
/**
* Lets the CanvasPanel know that the user is interacting with a node.
*
* @param InNodeToDrag The node that the user wants to drag
* @param GrabOffset Where within the node the user grabbed relative to split between inputs and outputs.
*/
virtual void OnBeginNodeInteraction ( const TSharedRef < SNode > & InNodeToDrag , const FVector2D & GrabOffset ) ;
/**
* Lets the CanvasPanel know that the user has ended interacting with a node.
*
* @param InNodeToDrag The node that the user was to dragging
*/
virtual void OnEndNodeInteraction ( const TSharedRef < SNode > & InNodeToDrag ) ;
/** Figure out which nodes intersect the marquee rectangle */
void FindNodesAffectedByMarquee ( FGraphPanelSelectionSet & OutAffectedNodes ) const ;
/**
* Apply the marquee operation to the current selection
*
* @param InMarquee The marquee operation to apply.
* @param CurrentSelection The selection before the marquee operation.
* @param OutNewSelection The selection resulting from Marquee being applied to CurrentSelection.
*/
static void ApplyMarqueeSelection ( const FMarqueeOperation & InMarquee , const FGraphPanelSelectionSet & CurrentSelection , FGraphPanelSelectionSet & OutNewSelection ) ;
/**
* On the next tick, centers and selects the widget associated with the object if it exists
*
* @param ObjectToSelect The object to select, and potentially center on
* @param bCenter Whether or not to center the graph node
*/
void SelectAndCenterObject ( const UObject * ObjectToSelect , bool bCenter ) ;
2015-05-01 06:38:56 -04:00
/**
* On the next tick, centers the widget associated with the object if it exists
*
* @param ObjectToCenter The object to center
*/
void CenterObject ( const UObject * ObjectToCenter ) ;
2014-03-14 14:13:41 -04:00
/** Add a slot to the CanvasPanel dynamically */
virtual void AddGraphNode ( const TSharedRef < SNode > & NodeToAdd ) ;
/** Remove all nodes from the panel */
virtual void RemoveAllNodes ( ) ;
/** Populate visibile children array */
virtual void PopulateVisibleChildren ( const FGeometry & AllottedGeometry ) ;
2014-07-22 04:03:40 -04:00
/** Arrange child nodes - allows derived classes to supply non-node children in OnArrangeChildren */
virtual void ArrangeChildNodes ( const FGeometry & AllottedGeometry , FArrangedChildren & ArrangedChildren ) const ;
2014-03-14 14:13:41 -04:00
// Paint the background as lines
2017-06-19 20:27:30 -04:00
void PaintBackgroundAsLines ( const FSlateBrush * BackgroundImage , const FGeometry & AllottedGeometry , const FSlateRect & MyCullingRect , FSlateWindowElementList & OutDrawElements , int32 & DrawLayerId ) const ;
2014-03-14 14:13:41 -04:00
// Paint the well shadow (around the perimeter)
2017-06-19 20:27:30 -04:00
void PaintSurroundSunkenShadow ( const FSlateBrush * ShadowImage , const FGeometry & AllottedGeometry , const FSlateRect & MyCullingRect , FSlateWindowElementList & OutDrawElements , int32 DrawLayerId ) const ;
2014-03-14 14:13:41 -04:00
// Paint the marquee selection rectangle
2017-06-19 20:27:30 -04:00
void PaintMarquee ( const FGeometry & AllottedGeometry , const FSlateRect & MyCullingRect , FSlateWindowElementList & OutDrawElements , int32 DrawLayerId ) const ;
2014-03-14 14:13:41 -04:00
// Paint the software mouse if necessary
2017-06-19 20:27:30 -04:00
void PaintSoftwareCursor ( const FGeometry & AllottedGeometry , const FSlateRect & MyCullingRect , FSlateWindowElementList & OutDrawElements , int32 DrawLayerId ) const ;
2014-03-14 14:13:41 -04:00
// Paint a comment bubble
2017-06-19 20:27:30 -04:00
void PaintComment ( const FString & CommentText , const FGeometry & AllottedGeometry , const FSlateRect & MyCullingRect , FSlateWindowElementList & OutDrawElements , int32 DrawLayerId , const FLinearColor & CommentTinting , float & HeightAboveNode , const FWidgetStyle & InWidgetStyle ) const ;
2014-03-14 14:13:41 -04:00
/** Determines if a specified node is not visually relevant. */
bool IsNodeCulled ( const TSharedRef < SNode > & Node , const FGeometry & AllottedGeometry ) const ;
protected :
///////////
// INTERFACE TO IMPLEMENT
///////////
/** @return the widget in the summoned context menu that should be focused. */
virtual TSharedPtr < SWidget > OnSummonContextMenu ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent ) { return TSharedPtr < SWidget > ( ) ; }
virtual bool OnHandleLeftMouseRelease ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent ) { return false ; }
protected :
2015-05-01 06:38:56 -04:00
/**
* Get the bounds of the given node
* @return True if successful
*/
2015-08-14 09:34:31 -04:00
bool GetBoundsForNode ( const UObject * InNode , /*out*/ FVector2D & MinCorner , /*out*/ FVector2D & MaxCorner , float Padding = 0.0f ) const ;
2015-05-01 06:38:56 -04:00
2014-03-14 14:13:41 -04:00
/**
* Get the bounds of the selected nodes
* @param bSelectionSetOnly If true, limits the query to just the selected nodes. Otherwise it does all nodes.
* @return True if successful
*/
bool GetBoundsForNodes ( bool bSelectionSetOnly , /*out*/ FVector2D & MinCorner , /*out*/ FVector2D & MaxCorner , float Padding = 0.0f ) ;
/**
* Scroll the view to the desired location
* @return true when the desired location is reached
*/
bool ScrollToLocation ( const FGeometry & MyGeometry , FVector2D DesiredCenterPosition , const float InDeltaTime ) ;
/**
* Zoom to fit the desired size
* @return true when zoom fade has completed & fits the desired size
*/
bool ZoomToLocation ( const FVector2D & CurrentSizeWithoutZoom , const FVector2D & DesiredSize , bool bDoneScrolling ) ;
/**
* Change zoom level by the specified zoom level delta, about the specified origin.
*/
void ChangeZoomLevel ( int32 ZoomLevelDelta , const FVector2D & WidgetSpaceZoomOrigin , bool bOverrideZoomLimiting ) ;
// Should be called whenever the zoom level has changed
void PostChangedZoom ( ) ;
2016-07-08 14:59:19 -04:00
// Fires up a per-tick function to zoom the graph to fit
void RequestZoomToFit ( ) ;
// Cancels any active zoom-to-fit action
void CancelZoomToFit ( ) ;
2021-05-18 06:44:40 -04:00
public :
// Sets the zoom levels container
template < typename T >
void SetZoomLevelsContainer ( )
{
ZoomLevels = MakeUnique < T > ( ) ;
OldZoomAmount = ZoomLevels - > GetZoomAmount ( ZoomLevel ) ;
ZoomLevel = PreviousZoomLevel = ZoomLevels - > GetNearestZoomLevel ( OldZoomAmount ) ;
PostChangedZoom ( ) ;
}
2014-03-14 14:13:41 -04:00
protected :
2021-05-18 06:44:40 -04:00
2014-03-14 14:13:41 -04:00
// The interface for mapping ZoomLevel values to actual node scaling values
2016-11-22 18:45:44 -05:00
TUniquePtr < FZoomLevelsContainer > ZoomLevels ;
2014-03-14 14:13:41 -04:00
/** The position within the graph at which the user is looking */
FVector2D ViewOffset ;
/** The position within the graph at which the user was looking last tick */
FVector2D OldViewOffset ;
/** How zoomed in/out we are. e.g. 0.25f results in quarter-sized nodes. */
int32 ZoomLevel ;
/** Previous Zoom Level */
int32 PreviousZoomLevel ;
/** The actual scalar zoom amount last tick */
float OldZoomAmount ;
/** Are we panning the view at the moment? */
bool bIsPanning ;
2014-07-17 15:10:02 -04:00
/** Are we zooming the view with trackpad at the moment? */
bool bIsZoomingWithTrackpad ;
2014-03-14 14:13:41 -04:00
/** The graph node widgets owned by this panel */
TSlotlessChildren < SNode > Children ;
TSlotlessChildren < SNode > VisibleChildren ;
/** The node that the user is dragging. Null when they are not dragging a node. */
TWeakPtr < SNode > NodeUnderMousePtr ;
/** Where in the title the user grabbed to initiate the drag */
FVector2D NodeGrabOffset ;
/** The total distance that the mouse has been dragged while down */
float TotalMouseDelta ;
2018-12-03 09:54:12 -05:00
/** The additive X and Y components of mouse drag (used when zooming) */
float TotalMouseDeltaXY ;
2014-03-14 14:13:41 -04:00
/** Offset in the panel the user started the LMB+RMB zoom from */
FVector2D ZoomStartOffset ;
2014-07-08 18:23:55 -04:00
/** Cumulative magnify delta from trackpad gesture */
float TotalGestureMagnify ;
2014-03-14 14:13:41 -04:00
public :
/** Nodes selected in this instance of the editor; the selection is per-instance of the GraphEditor */
FGraphSelectionManager SelectionManager ;
protected :
/** A pending marquee operation if it's active */
FMarqueeOperation Marquee ;
/** Is the graph editable (can nodes be moved, etc...)? */
TAttribute < bool > IsEditable ;
/** Given a node, find the corresponding widget */
TMap < UObject * , TSharedRef < SNode > > NodeToWidgetLookup ;
2014-09-30 14:13:40 -04:00
/** If not empty and a part of this panel, this node will be selected and brought into view on the next Tick */
TSet < const UObject * > DeferredSelectionTargetObjects ;
2014-03-14 14:13:41 -04:00
/** If non-null and a part of this panel, this node will be brought into view on the next Tick */
const UObject * DeferredMovementTargetObject ;
/** Deferred zoom to selected node extents */
bool bDeferredZoomToSelection ;
/** Deferred zoom to node extents */
bool bDeferredZoomToNodeExtents ;
/** Zoom selection padding */
float ZoomPadding ;
/** Allow continous zoom interpolation? */
bool bAllowContinousZoomInterpolation ;
/** Teleport immediately, or smoothly scroll when doing a deferred zoom */
bool bTeleportInsteadOfScrollingWhenZoomingToFit ;
/** Fade on zoom for graph */
FCurveSequence ZoomLevelGraphFade ;
/** Curve that handles fading the 'Zoom +X' text */
FCurveSequence ZoomLevelFade ;
/** The position where we should paste when a user executes the paste command. */
FVector2D PastePosition ;
/** Position to pan to */
FVector2D DeferredPanPosition ;
/** true if pending request for deferred panning */
bool bRequestDeferredPan ;
/** The current position of the software cursor */
FVector2D SoftwareCursorPosition ;
/** Whether the software cursor should be drawn */
bool bShowSoftwareCursor ;
/** Current LOD level for nodes/pins */
EGraphRenderingLOD : : Type CurrentLOD ;
/** Invoked when the user may be attempting to spawn a node using a shortcut */
SGraphEditor : : FOnSpawnNodeByShortcut OnSpawnNodeByShortcut ;
2015-03-17 11:36:28 -04:00
/** The last key chord detected in this graph panel */
FInputChord LastKeyChordDetected ;
2014-04-02 18:09:23 -04:00
/** The current transaction for undo/redo */
TSharedPtr < FScopedTransaction > ScopedTransactionPtr ;
2014-12-17 16:07:57 -05:00
2017-08-21 15:05:19 -04:00
/** Cached geometry for use within the active timer */
FGeometry CachedGeometry ;
2020-03-11 17:07:04 -04:00
/** A flag to detect when a visual update is pending to prevent deferred
commands like zoom to fit from running when there are no widgets */
bool bVisualUpdatePending ;
2021-05-18 15:26:27 -04:00
/** Node positions pre-drag, used to limit transaction creation on drag */
TMap < TWeakPtr < SNode > , FVector2D > OriginalNodePositions ;
2020-03-11 17:07:04 -04:00
2014-12-17 16:07:57 -05:00
private :
2014-12-19 17:44:49 -05:00
/** Active timer that handles deferred zooming until the target zoom is reached */
EActiveTimerReturnType HandleZoomToFit ( double InCurrentTime , float InDeltaTime ) ;
2014-12-17 16:07:57 -05:00
private :
2014-12-19 17:44:49 -05:00
/** The handle to the active timer */
TWeakPtr < FActiveTimerHandle > ActiveTimerHandle ;
2014-12-17 16:07:57 -05:00
/** Zoom target rectangle */
FVector2D ZoomTargetTopLeft ;
FVector2D ZoomTargetBottomRight ;
2018-03-27 14:27:07 -04:00
/** Current view bookmark info */
FGuid CurrentBookmarkGuid ;
2014-03-14 14:13:41 -04:00
} ;