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 "TokenizedMessage.h"
# include "Editor/UnrealEd/Public/DragAndDrop/ActorDragDropGraphEdOp.h"
# include "Editor/UnrealEd/Public/DragAndDrop/AssetDragDropOp.h"
# include "Editor/Persona/Public/BoneDragDropOp.h"
# include "Editor/UnrealEd/Public/Kismet2/BlueprintEditorUtils.h"
# include "SLevelOfDetailBranchNode.h"
# include "IDocumentation.h"
2014-09-05 07:39:52 -04:00
# include "TutorialMetaData.h"
2014-10-14 22:50:06 -04:00
# include "SGraphPanel.h"
# include "SInlineEditableTextBlock.h"
2014-03-14 14:13:41 -04:00
/////////////////////////////////////////////////////
// SNodeTitle
void SNodeTitle : : Construct ( const FArguments & InArgs , UEdGraphNode * InNode )
{
GraphNode = InNode ;
ExtraLineStyle = InArgs . _ExtraLineStyle ;
// If the user set the text, use it, otherwise use the node title by default
if ( InArgs . _Text . IsSet ( ) )
{
TitleText = InArgs . _Text ;
}
else
{
2014-04-23 18:30:37 -04:00
TitleText = TAttribute < FText > ( this , & SNodeTitle : : GetNodeTitle ) ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 18:30:37 -04:00
CachedTitle = TitleText . Get ( ) ;
2014-03-14 14:13:41 -04:00
RebuildWidget ( ) ;
}
---- 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 SNodeTitle : : Tick ( const FGeometry & AllottedGeometry , const double InCurrentTime , const float InDeltaTime )
2014-03-14 14:13:41 -04:00
{
// Checks to see if the cached string is valid, and if not, updates it.
2014-04-23 19:34:38 -04:00
if ( TitleText . Get ( ) . CompareTo ( CachedTitle ) ! = 0 )
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:30:37 -04:00
CachedTitle = TitleText . Get ( ) ;
2014-03-14 14:13:41 -04:00
RebuildWidget ( ) ;
}
}
2014-04-23 18:30:37 -04:00
FText SNodeTitle : : GetNodeTitle ( ) const
2014-03-14 14:13:41 -04:00
{
return ( GraphNode ! = NULL )
? GraphNode - > GetNodeTitle ( ENodeTitleType : : FullTitle )
2014-04-23 18:30:37 -04:00
: NSLOCTEXT ( " GraphEditor " , " NullNode " , " Null Node " ) ;
2014-03-14 14:13:41 -04:00
}
FText SNodeTitle : : GetHeadTitle ( ) const
{
2014-05-15 17:34:14 -04:00
return GraphNode - > bCanRenameNode ? GraphNode - > GetNodeTitle ( ENodeTitleType : : EditableTitle ) : CachedHeadTitle ;
2014-03-14 14:13:41 -04:00
}
void SNodeTitle : : RebuildWidget ( )
{
// Create the box to contain the lines
TSharedPtr < SVerticalBox > VerticalBox ;
this - > ChildSlot
[
SAssignNew ( VerticalBox , SVerticalBox )
] ;
// Break the title into lines
TArray < FString > Lines ;
2014-09-15 21:53:46 -04:00
const FString CachedTitleString = CachedTitle . ToString ( ) . Replace ( TEXT ( " \r " ) , TEXT ( " " ) ) ;
CachedTitleString . ParseIntoArray ( & Lines , TEXT ( " \n " ) , false ) ;
2014-03-14 14:13:41 -04:00
2014-09-15 19:53:19 -04:00
if ( Lines . Num ( ) )
2014-03-14 14:13:41 -04:00
{
CachedHeadTitle = FText : : FromString ( Lines [ 0 ] ) ;
}
2014-09-15 19:53:19 -04:00
// Pad the height of multi-line node titles to be a multiple of the graph snap grid taller than
// single-line nodes, so the pins will still line up if you place the node N cell snaps above
if ( Lines . Num ( ) > 1 )
{
// Note: This code a little fragile, and will need to be updated if the font or padding of titles
// changes in the future, but the failure mode is just a slight misalignment.
const int32 EstimatedExtraHeight = ( Lines . Num ( ) - 1 ) * 13 ;
const int32 SnapSize = ( int32 ) SNodePanel : : GetSnapGridSize ( ) ;
const int32 PadSize = SnapSize - ( EstimatedExtraHeight % SnapSize ) ;
if ( PadSize < SnapSize )
{
VerticalBox - > AddSlot ( )
[
SNew ( SSpacer )
. Size ( FVector2D ( 1.0f , PadSize ) )
] ;
}
}
2014-03-14 14:13:41 -04:00
// Make a separate widget for each line, using a less obvious style for subsequent lines
for ( int32 Index = 1 ; Index < Lines . Num ( ) ; + + Index )
{
VerticalBox - > AddSlot ( )
. AutoHeight ( )
[
SNew ( STextBlock )
. TextStyle ( FEditorStyle : : Get ( ) , ExtraLineStyle )
2015-01-07 09:52:40 -05:00
. Text ( FText : : FromString ( Lines [ Index ] ) )
2014-03-14 14:13:41 -04:00
] ;
}
}
/////////////////////////////////////////////////////
// SGraphNode
// Check whether drag and drop functionality is permitted on the given node
bool SGraphNode : : CanAllowInteractionUsingDragDropOp ( const UEdGraphNode * GraphNodePtr , const TSharedPtr < FActorDragDropOp > & DragDropOp )
{
bool bReturn = false ;
//Allow interaction only if this node is a literal type object.
//Only change actor reference if a single actor reference is dragged from the outliner.
if ( GraphNodePtr - > IsA ( UK2Node_Literal : : StaticClass ( ) ) & & DragDropOp - > Actors . Num ( ) = = 1 )
{
bReturn = true ;
}
return bReturn ;
}
void SGraphNode : : SetIsEditable ( TAttribute < bool > InIsEditable )
{
IsEditable = InIsEditable ;
2014-10-14 15:43:17 -04:00
int32 i ;
for ( i = 0 ; i < InputPins . Num ( ) ; + + i )
{
InputPins [ i ] - > SetIsEditable ( IsEditable ) ;
}
for ( i = 0 ; i < OutputPins . Num ( ) ; + + i )
{
OutputPins [ i ] - > SetIsEditable ( IsEditable ) ;
}
2014-03-14 14:13:41 -04:00
}
2014-06-18 05:04:59 -04:00
bool SGraphNode : : IsNodeEditable ( ) const
{
2014-06-18 14:17:13 -04:00
bool bIsEditable = OwnerGraphPanelPtr . IsValid ( ) ? OwnerGraphPanelPtr . Pin ( ) - > IsGraphEditable ( ) : true ;
return IsEditable . Get ( ) & & bIsEditable ;
2014-06-18 05:04:59 -04:00
}
2014-03-14 14:13:41 -04:00
/** Set event when node is double clicked */
void SGraphNode : : SetDoubleClickEvent ( FSingleNodeEvent InDoubleClickEvent )
{
OnDoubleClick = InDoubleClickEvent ;
}
void SGraphNode : : SetVerifyTextCommitEvent ( FOnNodeVerifyTextCommit InOnVerifyTextCommit )
{
OnVerifyTextCommit = InOnVerifyTextCommit ;
}
void SGraphNode : : SetTextCommittedEvent ( FOnNodeTextCommitted InOnTextCommitted )
{
OnTextCommitted = InOnTextCommitted ;
}
void SGraphNode : : SetDisallowedPinConnectionEvent ( SGraphEditor : : FOnDisallowedPinConnection InOnDisallowedPinConnection )
{
OnDisallowedPinConnection = InOnDisallowedPinConnection ;
}
void SGraphNode : : OnDragEnter ( const FGeometry & MyGeometry , const FDragDropEvent & DragDropEvent )
{
2014-04-23 18:00:50 -04:00
TSharedPtr < FDragDropOperation > Operation = DragDropEvent . GetOperation ( ) ;
if ( ! Operation . IsValid ( ) )
{
return ;
}
2014-03-14 14:13:41 -04:00
// Is someone dragging a connection?
2014-04-23 18:00:50 -04:00
if ( Operation - > IsOfType < FGraphEditorDragDropAction > ( ) )
2014-03-14 14:13:41 -04:00
{
// Inform the Drag and Drop operation that we are hovering over this pin.
2014-04-23 18:00:50 -04:00
TSharedPtr < FGraphEditorDragDropAction > DragConnectionOp = StaticCastSharedPtr < FGraphEditorDragDropAction > ( Operation ) ;
2014-03-14 14:13:41 -04:00
DragConnectionOp - > SetHoveredNode ( SharedThis ( this ) ) ;
}
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 > DragConnectionOp = StaticCastSharedPtr < FActorDragDropGraphEdOp > ( Operation ) ;
2014-03-14 14:13:41 -04:00
if ( GraphNode - > IsA ( UK2Node_Literal : : StaticClass ( ) ) )
{
//Show tool tip only if a single actor is dragged
if ( DragConnectionOp - > Actors . Num ( ) = = 1 )
{
UK2Node_Literal * LiteralNode = CastChecked < UK2Node_Literal > ( GraphNode ) ;
//Check whether this node is already referencing the same actor dragged from outliner
if ( LiteralNode - > GetObjectRef ( ) ! = DragConnectionOp - > Actors [ 0 ] . Get ( ) )
{
DragConnectionOp - > SetToolTip ( FActorDragDropGraphEdOp : : ToolTip_Compatible ) ;
}
}
else
{
//For more that one actor dragged on to a literal node, show tooltip as incompatible
DragConnectionOp - > SetToolTip ( FActorDragDropGraphEdOp : : ToolTip_MultipleSelection_Incompatible ) ;
}
}
else
{
DragConnectionOp - > SetToolTip ( ( DragConnectionOp - > Actors . Num ( ) = = 1 ) ? FActorDragDropGraphEdOp : : ToolTip_Incompatible : FActorDragDropGraphEdOp : : ToolTip_MultipleSelection_Incompatible ) ;
}
}
2014-04-23 18:00:50 -04:00
else if ( Operation - > IsOfType < FBoneDragDropOp > ( ) )
2014-03-14 14:13:41 -04:00
{
//@TODO: A2REMOVAL: No support for A3 nodes handling this drag-drop op yet!
}
}
void SGraphNode : : OnDragLeave ( const FDragDropEvent & DragDropEvent )
{
2014-04-23 18:00:50 -04:00
TSharedPtr < FDragDropOperation > Operation = DragDropEvent . GetOperation ( ) ;
if ( ! Operation . IsValid ( ) )
{
return ;
}
2014-03-14 14:13:41 -04:00
// Is someone dragging a connection?
2014-04-23 18:00:50 -04:00
if ( Operation - > IsOfType < FGraphEditorDragDropAction > ( ) )
2014-03-14 14:13:41 -04:00
{
// Inform the Drag and Drop operation that we are not hovering any pins
2014-04-23 18:00:50 -04:00
TSharedPtr < FGraphEditorDragDropAction > DragConnectionOp = StaticCastSharedPtr < FGraphEditorDragDropAction > ( Operation ) ;
2014-03-14 14:13:41 -04:00
DragConnectionOp - > SetHoveredNode ( TSharedPtr < SGraphNode > ( NULL ) ) ;
}
2014-04-23 18:00:50 -04:00
else if ( Operation - > IsOfType < FActorDragDropGraphEdOp > ( ) )
2014-03-14 14:13:41 -04:00
{
//Default tool tip
2014-04-23 18:00:50 -04:00
TSharedPtr < FActorDragDropGraphEdOp > DragConnectionOp = StaticCastSharedPtr < FActorDragDropGraphEdOp > ( Operation ) ;
2014-03-14 14:13:41 -04:00
DragConnectionOp - > ResetToDefaultToolTip ( ) ;
}
2014-04-23 18:00:50 -04:00
else if ( Operation - > IsOfType < FAssetDragDropOp > ( ) )
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:00:50 -04:00
TSharedPtr < FAssetDragDropOp > AssetOp = StaticCastSharedPtr < FAssetDragDropOp > ( Operation ) ;
2014-03-14 14:13:41 -04:00
AssetOp - > ResetToDefaultToolTip ( ) ;
}
2014-04-23 18:00:50 -04:00
else if ( Operation - > IsOfType < FBoneDragDropOp > ( ) )
2014-03-14 14:13:41 -04:00
{
//@TODO: A2REMOVAL: No support for A3 nodes handling this drag-drop op yet!
}
}
FReply SGraphNode : : OnDragOver ( const FGeometry & MyGeometry , const FDragDropEvent & DragDropEvent )
{
2014-04-23 18:00:50 -04:00
TSharedPtr < FAssetDragDropOp > AssetOp = DragDropEvent . GetOperationAs < FAssetDragDropOp > ( ) ;
if ( AssetOp . IsValid ( ) )
2014-03-14 14:13:41 -04:00
{
if ( GraphNode ! = NULL & & GraphNode - > GetSchema ( ) ! = NULL )
{
bool bOkIcon = false ;
FString TooltipText ;
GraphNode - > GetSchema ( ) - > GetAssetsNodeHoverMessage ( AssetOp - > AssetData , GraphNode , TooltipText , bOkIcon ) ;
2014-06-18 05:04:59 -04:00
bool bReadOnly = OwnerGraphPanelPtr . IsValid ( ) ? ! OwnerGraphPanelPtr . Pin ( ) - > IsGraphEditable ( ) : false ;
bOkIcon = bReadOnly ? false : bOkIcon ;
2014-03-14 14:13:41 -04:00
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 ( ) ;
}
return FReply : : Unhandled ( ) ;
}
/** Given a coordinate in SGraphPanel space (i.e. panel widget space), return the same coordinate in graph space while taking zoom and panning into account */
FVector2D SGraphNode : : NodeCoordToGraphCoord ( const FVector2D & NodeSpaceCoordinate ) const
{
TSharedPtr < SGraphPanel > OwnerCanvas = OwnerGraphPanelPtr . Pin ( ) ;
if ( OwnerCanvas . IsValid ( ) )
{
//@TODO: NodeSpaceCoordinate != PanelCoordinate
FVector2D PanelSpaceCoordinate = NodeSpaceCoordinate ;
return OwnerCanvas - > PanelCoordToGraphCoord ( PanelSpaceCoordinate ) ;
}
else
{
return FVector2D : : ZeroVector ;
}
}
FReply SGraphNode : : OnDrop ( const FGeometry & MyGeometry , const FDragDropEvent & DragDropEvent )
{
2014-06-18 05:04:59 -04:00
bool bReadOnly = OwnerGraphPanelPtr . IsValid ( ) ? ! OwnerGraphPanelPtr . Pin ( ) - > IsGraphEditable ( ) : false ;
2014-04-23 18:00:50 -04:00
TSharedPtr < FDragDropOperation > Operation = DragDropEvent . GetOperation ( ) ;
2014-06-18 05:04:59 -04:00
if ( ! Operation . IsValid ( ) | | bReadOnly )
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:00:50 -04:00
return FReply : : Unhandled ( ) ;
}
// Is someone dropping a connection onto this node?
if ( Operation - > IsOfType < FGraphEditorDragDropAction > ( ) )
{
TSharedPtr < FGraphEditorDragDropAction > DragConnectionOp = StaticCastSharedPtr < FGraphEditorDragDropAction > ( Operation ) ;
2014-03-14 14:13:41 -04:00
const FVector2D NodeAddPosition = NodeCoordToGraphCoord ( MyGeometry . AbsoluteToLocal ( DragDropEvent . GetScreenSpacePosition ( ) ) ) ;
FReply Result = DragConnectionOp - > DroppedOnNode ( DragDropEvent . GetScreenSpacePosition ( ) , NodeAddPosition ) ;
if ( Result . IsEventHandled ( ) & & ( GraphNode ! = NULL ) )
{
GraphNode - > GetGraph ( ) - > NotifyGraphChanged ( ) ;
}
return Result ;
}
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 > DragConnectionOp = StaticCastSharedPtr < FActorDragDropGraphEdOp > ( Operation ) ;
2014-03-14 14:13:41 -04:00
if ( CanAllowInteractionUsingDragDropOp ( GraphNode , DragConnectionOp ) )
{
UK2Node_Literal * LiteralNode = CastChecked < UK2Node_Literal > ( GraphNode ) ;
//Check whether this node is already referencing the same actor
if ( LiteralNode - > GetObjectRef ( ) ! = DragConnectionOp - > Actors [ 0 ] . Get ( ) )
{
//Replace literal node's object reference
LiteralNode - > SetObjectRef ( DragConnectionOp - > Actors [ 0 ] . Get ( ) ) ;
2014-08-14 15:49:56 -04:00
UBlueprint * Blueprint = FBlueprintEditorUtils : : FindBlueprintForGraph ( CastChecked < UEdGraph > ( GraphNode - > GetOuter ( ) ) ) ;
2014-08-14 15:50:32 -04:00
if ( Blueprint ! = nullptr )
{
FBlueprintEditorUtils : : MarkBlueprintAsModified ( Blueprint ) ;
}
2014-03-14 14:13:41 -04:00
}
}
return FReply : : Handled ( ) ;
}
2014-04-23 18:00:50 -04:00
else if ( Operation - > IsOfType < FAssetDragDropOp > ( ) )
2014-03-14 14:13:41 -04:00
{
UEdGraphNode * Node = GetNodeObj ( ) ;
if ( Node ! = NULL & & Node - > GetSchema ( ) ! = NULL )
{
2014-04-23 18:00:50 -04:00
TSharedPtr < FAssetDragDropOp > AssetOp = StaticCastSharedPtr < FAssetDragDropOp > ( Operation ) ;
2014-03-14 14:13:41 -04:00
Node - > GetSchema ( ) - > DroppedAssetsOnNode ( AssetOp - > AssetData , DragDropEvent . GetScreenSpacePosition ( ) , Node ) ;
}
return FReply : : Handled ( ) ;
}
2014-04-23 18:00:50 -04:00
else if ( Operation - > IsOfType < FBoneDragDropOp > ( ) )
2014-03-14 14:13:41 -04:00
{
//@TODO: A2REMOVAL: No support for A3 nodes handling this drag-drop op yet!
}
return FReply : : Unhandled ( ) ;
}
/**
* The system calls this method to notify the widget that a mouse button was pressed within it . This event is bubbled .
*
* @ param MyGeometry The Geometry of the widget receiving the event
* @ param MouseEvent Information about the input event
*
* @ return Whether the event was handled along with possible requests for the system to take action .
*/
FReply SGraphNode : : OnMouseButtonDown ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent )
{
return FReply : : Unhandled ( ) ;
}
// The system calls this method to notify the widget that a mouse button was release within it. This event is bubbled.
FReply SGraphNode : : OnMouseButtonUp ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent )
{
return FReply : : Unhandled ( ) ;
}
// Called when a mouse button is double clicked. Override this in derived classes
FReply SGraphNode : : OnMouseButtonDoubleClick ( const FGeometry & InMyGeometry , const FPointerEvent & InMouseEvent )
{
2014-04-02 18:09:23 -04:00
if ( InMouseEvent . IsMouseButtonDown ( EKeys : : LeftMouseButton ) )
{
OnDoubleClick . ExecuteIfBound ( GraphNode ) ;
return FReply : : Handled ( ) ;
}
return FReply : : Unhandled ( ) ;
2014-03-14 14:13:41 -04:00
}
Slate: Refactored core Slate implementation into SlateCore module in preparation for UMG.
Other Updates:
- The WidgetReflector is now in its own module as well. It will be converted to a plug-in later.
- The Public API of both Slate and SlateCore has largely been reorganized for better discoverabilty. More cleanup work is needed.
- Added a lot of missing API documentation and fixed existing ones. More and better documentation is needed.
- Removed dead code, fixed a couple things I stubled upon, and conformed to coding guidelines (NULL vs nullptr, line breaks, etc.)
Upgrade Notes:
- The Slate Remote Server is currently disabled - will be re-enabled shortly!
- If your module previously had a module dependency to 'Slate', it now also needs a PrivateModuleDependency to 'SlateCore' in its Build.cs file.
- If your module exposes in any of its Public header files types that are now declared in SlateCore, it needs a PublicModuleDependency to 'SlateCore'
- The ToolTip property type on SWidget has changed from SToolTip to IToolTip; change local variables to TSharedPtr<IToolTip> instead of TSharedPtr<SToolTip> where needed
- IToolTip is not a widget. If you need access to the actual widget that represents the tool tip, use IToolTip::AsWidget(); If you need access to the tool tip's content, use IToolTip::GetContentWidget()
Troubleshooting:
- After syncing to this changelist you may have to clean your /Engine/Intermediate/Build/ directory and rebuild your entire project
- If in your project you are getting linker errors for unresolved types that are now declared in SlateCore, you may be missing a dependency to 'SlateCore'
- If in the Engine code you are getting linker errors for unresolved types that are now declared in SlateCore, you may need to rebuild the entire Engine
[CL 2057118 by Max Preussner in Main branch]
2014-04-26 15:07:24 -04:00
TSharedPtr < IToolTip > SGraphNode : : GetToolTip ( )
2014-03-14 14:13:41 -04:00
{
Slate: Refactored core Slate implementation into SlateCore module in preparation for UMG.
Other Updates:
- The WidgetReflector is now in its own module as well. It will be converted to a plug-in later.
- The Public API of both Slate and SlateCore has largely been reorganized for better discoverabilty. More cleanup work is needed.
- Added a lot of missing API documentation and fixed existing ones. More and better documentation is needed.
- Removed dead code, fixed a couple things I stubled upon, and conformed to coding guidelines (NULL vs nullptr, line breaks, etc.)
Upgrade Notes:
- The Slate Remote Server is currently disabled - will be re-enabled shortly!
- If your module previously had a module dependency to 'Slate', it now also needs a PrivateModuleDependency to 'SlateCore' in its Build.cs file.
- If your module exposes in any of its Public header files types that are now declared in SlateCore, it needs a PublicModuleDependency to 'SlateCore'
- The ToolTip property type on SWidget has changed from SToolTip to IToolTip; change local variables to TSharedPtr<IToolTip> instead of TSharedPtr<SToolTip> where needed
- IToolTip is not a widget. If you need access to the actual widget that represents the tool tip, use IToolTip::AsWidget(); If you need access to the tool tip's content, use IToolTip::GetContentWidget()
Troubleshooting:
- After syncing to this changelist you may have to clean your /Engine/Intermediate/Build/ directory and rebuild your entire project
- If in your project you are getting linker errors for unresolved types that are now declared in SlateCore, you may be missing a dependency to 'SlateCore'
- If in the Engine code you are getting linker errors for unresolved types that are now declared in SlateCore, you may need to rebuild the entire Engine
[CL 2057118 by Max Preussner in Main branch]
2014-04-26 15:07:24 -04:00
TSharedPtr < IToolTip > CurrentTooltip = SWidget : : GetToolTip ( ) ;
2014-03-14 14:13:41 -04:00
if ( ! CurrentTooltip . IsValid ( ) )
{
TSharedPtr < SToolTip > ComplexTooltip = GetComplexTooltip ( ) ;
if ( ComplexTooltip . IsValid ( ) )
{
SetToolTip ( ComplexTooltip ) ;
bProvidedComplexTooltip = true ;
}
}
return SWidget : : GetToolTip ( ) ;
}
void SGraphNode : : OnToolTipClosing ( )
{
if ( bProvidedComplexTooltip )
{
SetToolTip ( NULL ) ;
bProvidedComplexTooltip = false ;
}
}
void SGraphNode : : Tick ( const FGeometry & AllottedGeometry , const double InCurrentTime , const float InDeltaTime )
{
CachedUnscaledPosition = AllottedGeometry . AbsolutePosition / AllottedGeometry . Scale ;
SNodePanel : : SNode : : Tick ( AllottedGeometry , InCurrentTime , InDeltaTime ) ;
}
bool SGraphNode : : IsSelectedExclusively ( ) const
{
TSharedPtr < SGraphPanel > OwnerPanel = OwnerGraphPanelPtr . Pin ( ) ;
2014-10-17 05:01:49 -04:00
if ( ! OwnerPanel - > HasKeyboardFocus ( ) | | OwnerPanel - > SelectionManager . GetSelectedNodes ( ) . Num ( ) > 1 )
2014-03-14 14:13:41 -04:00
{
return false ;
}
return OwnerPanel - > SelectionManager . IsNodeSelected ( GraphNode ) ;
}
/** @param OwnerPanel The GraphPanel that this node belongs to */
void SGraphNode : : SetOwner ( const TSharedRef < SGraphPanel > & OwnerPanel )
{
check ( ! OwnerGraphPanelPtr . IsValid ( ) ) ;
OwnerGraphPanelPtr = OwnerPanel ;
GraphNode - > NodeWidget = SharedThis ( this ) ;
/*Once we have an owner, and if hide Unused pins is enabled, we need to remake our pins to drop the hidden ones*/
if ( OwnerGraphPanelPtr . Pin ( ) - > GetPinVisibility ( ) ! = SGraphEditor : : Pin_Show
& & LeftNodeBox . IsValid ( )
& & RightNodeBox . IsValid ( ) )
{
this - > LeftNodeBox - > ClearChildren ( ) ;
this - > RightNodeBox - > ClearChildren ( ) ;
CreatePinWidgets ( ) ;
}
}
/** @param NewPosition The Node should be relocated to this position in the graph panel */
2014-05-06 04:45:44 -04:00
void SGraphNode : : MoveTo ( const FVector2D & NewPosition , FNodeSet & NodeFilter )
2014-03-14 14:13:41 -04:00
{
2014-05-06 04:45:44 -04:00
if ( ! NodeFilter . Find ( SharedThis ( this ) ) )
2014-03-14 14:13:41 -04:00
{
2014-05-06 04:45:44 -04:00
if ( GraphNode & & ! RequiresSecondPassLayout ( ) )
2014-03-14 14:13:41 -04:00
{
2014-05-06 04:45:44 -04:00
NodeFilter . Add ( SharedThis ( this ) ) ;
2014-04-02 18:09:23 -04:00
GraphNode - > Modify ( ) ;
2014-03-14 14:13:41 -04:00
GraphNode - > NodePosX = NewPosition . X ;
GraphNode - > NodePosY = NewPosition . Y ;
}
}
}
/** @return the Node's position within the graph */
FVector2D SGraphNode : : GetPosition ( ) const
{
return FVector2D ( GraphNode - > NodePosX , GraphNode - > NodePosY ) ;
}
FString SGraphNode : : GetEditableNodeTitle ( ) const
{
if ( GraphNode ! = NULL )
{
// Trying to catch a non-reproducible crash in this function
check ( GraphNode - > IsValidLowLevel ( ) ) ;
}
if ( GraphNode )
{
2014-04-23 18:30:37 -04:00
return GraphNode - > GetNodeTitle ( ENodeTitleType : : EditableTitle ) . ToString ( ) ;
2014-03-14 14:13:41 -04:00
}
return NSLOCTEXT ( " GraphEditor " , " NullNode " , " Null Node " ) . ToString ( ) ;
// Get the portion of the node that is actually editable text (may be a subsection of the title, or something else entirely)
return ( GraphNode ! = NULL )
2014-04-23 18:30:37 -04:00
? GraphNode - > GetNodeTitle ( ENodeTitleType : : EditableTitle ) . ToString ( )
2014-03-14 14:13:41 -04:00
: NSLOCTEXT ( " GraphEditor " , " NullNode " , " Null Node " ) . ToString ( ) ;
}
FText SGraphNode : : GetEditableNodeTitleAsText ( ) const
{
FString NewString = GetEditableNodeTitle ( ) ;
return FText : : FromString ( NewString ) ;
}
FString SGraphNode : : GetNodeComment ( ) const
{
return GetNodeObj ( ) - > NodeComment ;
}
UObject * SGraphNode : : GetObjectBeingDisplayed ( ) const
{
return GetNodeObj ( ) ;
}
FSlateColor SGraphNode : : GetNodeTitleColor ( ) const
{
FLinearColor NodeTitleColor = GraphNode - > IsDeprecated ( ) ? FLinearColor : : Red : GetNodeObj ( ) - > GetNodeTitleColor ( ) ;
NodeTitleColor . A = FadeCurve . GetLerp ( ) ;
return NodeTitleColor ;
}
FSlateColor SGraphNode : : GetNodeCommentColor ( ) const
{
return GetNodeObj ( ) - > GetNodeCommentColor ( ) ;
}
/** @return the tooltip to display when over the node */
FText SGraphNode : : GetNodeTooltip ( ) const
{
if ( GraphNode ! = NULL )
{
2014-04-23 18:30:37 -04:00
// Display the native title of the node when alt is held
if ( FSlateApplication : : Get ( ) . GetModifierKeys ( ) . IsAltDown ( ) )
{
2014-06-23 15:36:50 -04:00
return FText : : FromString ( GraphNode - > GetNodeTitle ( ENodeTitleType : : ListView ) . BuildSourceString ( ) ) ;
2014-04-23 18:30:37 -04:00
}
2014-09-03 18:14:09 -04:00
FText TooltipText = GraphNode - > GetTooltipText ( ) ;
2014-03-14 14:13:41 -04:00
if ( UEdGraph * Graph = GraphNode - > GetGraph ( ) )
{
// If the node resides in an intermediate graph, show the UObject name for debug purposes
if ( Graph - > HasAnyFlags ( RF_Transient ) )
{
2014-04-23 18:30:37 -04:00
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " NodeName " ) , FText : : FromString ( GraphNode - > GetName ( ) ) ) ;
Args . Add ( TEXT ( " TooltipText " ) , TooltipText ) ;
TooltipText = FText : : Format ( NSLOCTEXT ( " GraphEditor " , " GraphNodeTooltip " , " {NodeName} \n \n {TooltipText} " ) , Args ) ;
2014-03-14 14:13:41 -04:00
}
}
if ( TooltipText . IsEmpty ( ) )
{
2014-04-23 18:30:37 -04:00
TooltipText = GraphNode - > GetNodeTitle ( ENodeTitleType : : FullTitle ) ;
2014-03-14 14:13:41 -04:00
}
return TooltipText ;
}
else
{
return NSLOCTEXT ( " GraphEditor " , " InvalidGraphNode " , " <Invalid graph node> " ) ;
}
}
/** @return the node being observed by this widget*/
UEdGraphNode * SGraphNode : : GetNodeObj ( ) const
{
return GraphNode ;
}
2014-09-11 07:02:40 -04:00
TSharedRef < SGraphNode > SGraphNode : : GetNodeUnderMouse ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent )
{
return StaticCastSharedRef < SGraphNode > ( AsShared ( ) ) ;
}
2014-03-14 14:13:41 -04:00
TSharedPtr < SGraphPanel > SGraphNode : : GetOwnerPanel ( ) const
{
return OwnerGraphPanelPtr . Pin ( ) ;
}
void SGraphNode : : UpdateErrorInfo ( )
{
//Check for node errors/warnings
if ( GraphNode - > bHasCompilerMessage )
{
if ( GraphNode - > ErrorType < = EMessageSeverity : : Error )
{
ErrorMsg = FString ( TEXT ( " ERROR! " ) ) ;
ErrorColor = FEditorStyle : : GetColor ( " ErrorReporting.BackgroundColor " ) ;
}
else if ( GraphNode - > ErrorType < = EMessageSeverity : : Warning )
{
ErrorMsg = FString ( TEXT ( " WARNING! " ) ) ;
ErrorColor = FEditorStyle : : GetColor ( " ErrorReporting.WarningBackgroundColor " ) ;
}
else
{
ErrorMsg = FString ( TEXT ( " NOTE " ) ) ;
ErrorColor = FEditorStyle : : GetColor ( " InfoReporting.BackgroundColor " ) ;
}
}
else
{
ErrorColor = FLinearColor ( 0 , 0 , 0 ) ;
ErrorMsg . Empty ( ) ;
}
}
TSharedPtr < SWidget > SGraphNode : : SetupErrorReporting ( )
{
TSharedPtr < SErrorText > ErrorText ;
UpdateErrorInfo ( ) ;
// generate widget
2014-09-15 19:53:19 -04:00
SAssignNew ( ErrorText , SErrorText )
. BackgroundColor ( this , & SGraphNode : : GetErrorColor )
. ToolTipText ( this , & SGraphNode : : GetErrorMsgToolTip ) ;
2014-03-14 14:13:41 -04:00
ErrorReporting = ErrorText ;
ErrorReporting - > SetError ( ErrorMsg ) ;
return ErrorText ;
}
TSharedRef < SWidget > SGraphNode : : CreateTitleWidget ( TSharedPtr < SNodeTitle > NodeTitle )
{
return SAssignNew ( InlineEditableText , SInlineEditableTextBlock )
. Style ( FEditorStyle : : Get ( ) , " Graph.Node.NodeTitleInlineEditableText " )
. Text ( NodeTitle . Get ( ) , & SNodeTitle : : GetHeadTitle )
. OnVerifyTextChanged ( this , & SGraphNode : : OnVerifyNameTextChanged )
. OnTextCommitted ( this , & SGraphNode : : OnNameTextCommited )
. IsReadOnly ( this , & SGraphNode : : IsNameReadOnly )
. IsSelected ( this , & SGraphNode : : IsSelectedExclusively ) ;
}
/**
* Update this GraphNode to match the data that it is observing
*/
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SGraphNode : : UpdateGraphNode ( )
{
InputPins . Empty ( ) ;
OutputPins . Empty ( ) ;
// Reset variables that are going to be exposed, in case we are refreshing an already setup node.
RightNodeBox . Reset ( ) ;
LeftNodeBox . Reset ( ) ;
//
// ______________________
// | TITLE AREA |
// +-------+------+-------+
// | (>) L | | R (>) |
// | (>) E | | I (>) |
// | (>) F | | G (>) |
// | (>) T | | H (>) |
// | | | T (>) |
// |_______|______|_______|
//
TSharedPtr < SVerticalBox > MainVerticalBox ;
TSharedPtr < SWidget > ErrorText = SetupErrorReporting ( ) ;
TSharedPtr < SNodeTitle > NodeTitle = SNew ( SNodeTitle , GraphNode ) ;
// Get node icon
FLinearColor IconColor = FLinearColor : : White ;
const FSlateBrush * IconBrush = NULL ;
if ( GraphNode ! = NULL & & GraphNode - > ShowPaletteIconOnNode ( ) )
{
IconBrush = FEditorStyle : : GetBrush ( GraphNode - > GetPaletteIcon ( IconColor ) ) ;
}
TSharedRef < SOverlay > DefaultTitleAreaWidget =
SNew ( SOverlay )
+ SOverlay : : Slot ( )
[
SNew ( SImage )
. Image ( FEditorStyle : : GetBrush ( " Graph.Node.TitleGloss " ) )
]
+ SOverlay : : Slot ( )
. HAlign ( HAlign_Left )
. VAlign ( VAlign_Center )
[
SNew ( SBorder )
. BorderImage ( FEditorStyle : : GetBrush ( " Graph.Node.ColorSpill " ) )
// The extra margin on the right
// is for making the color spill stretch well past the node title
. Padding ( FMargin ( 10 , 5 , 30 , 3 ) )
. BorderBackgroundColor ( this , & SGraphNode : : GetNodeTitleColor )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. VAlign ( VAlign_Top )
. Padding ( FMargin ( 0.f , 0.f , 4.f , 0.f ) )
. AutoWidth ( )
[
SNew ( SImage )
. Image ( IconBrush )
. ColorAndOpacity ( IconColor )
]
+ SHorizontalBox : : Slot ( )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
[
CreateTitleWidget ( NodeTitle )
]
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
[
NodeTitle . ToSharedRef ( )
]
]
]
]
+ SOverlay : : Slot ( )
. VAlign ( VAlign_Top )
[
SNew ( SBorder )
. Visibility ( EVisibility : : HitTestInvisible )
. BorderImage ( FEditorStyle : : GetBrush ( " Graph.Node.TitleHighlight " ) )
[
SNew ( SSpacer )
. Size ( FVector2D ( 20 , 20 ) )
]
] ;
SetDefaultTitleAreaWidget ( DefaultTitleAreaWidget ) ;
TSharedRef < SWidget > TitleAreaWidget =
SNew ( SLevelOfDetailBranchNode )
. UseLowDetailSlot ( this , & SGraphNode : : UseLowDetailNodeTitles )
. LowDetail ( )
[
SNew ( SBorder )
. BorderImage ( FEditorStyle : : GetBrush ( " Graph.Node.ColorSpill " ) )
. Padding ( FMargin ( 75.0f , 22.0f ) ) // Saving enough space for a 'typical' title so the transition isn't quite so abrupt
. BorderBackgroundColor ( this , & SGraphNode : : GetNodeTitleColor )
]
. HighDetail ( )
[
DefaultTitleAreaWidget
] ;
if ( ! SWidget : : GetToolTip ( ) . IsValid ( ) )
{
TSharedRef < SToolTip > DefaultToolTip = IDocumentation : : Get ( ) - > CreateToolTip ( TAttribute < FText > ( this , & SGraphNode : : GetNodeTooltip ) , NULL , GraphNode - > GetDocumentationLink ( ) , GraphNode - > GetDocumentationExcerptName ( ) ) ;
SetToolTip ( DefaultToolTip ) ;
}
2014-09-05 07:39:52 -04:00
// Setup a meta tag for this node
FGraphNodeMetaData TagMeta ( TEXT ( " Graphnode " ) ) ;
PopulateMetaTag ( & TagMeta ) ;
2014-08-13 03:41:45 -04:00
2014-03-14 14:13:41 -04:00
TSharedPtr < SVerticalBox > InnerVerticalBox ;
this - > ContentScale . Bind ( this , & SGraphNode : : GetContentScale ) ;
2014-09-15 21:48:46 -04:00
2014-11-03 10:40:57 -05:00
this - > GetOrAddSlot ( ENodeZone : : Center )
2014-03-14 14:13:41 -04:00
. HAlign ( HAlign_Center )
. VAlign ( VAlign_Center )
[
SAssignNew ( MainVerticalBox , SVerticalBox )
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
[
2014-09-15 21:48:46 -04:00
SNew ( SOverlay )
2014-09-05 07:39:52 -04:00
. AddMetaData < FGraphNodeMetaData > ( TagMeta )
2014-09-15 21:48:46 -04:00
+ SOverlay : : Slot ( )
. Padding ( Settings - > GetNonPinNodeBodyPadding ( ) )
[
SNew ( SImage )
. Image ( FEditorStyle : : GetBrush ( " Graph.Node.Body " ) )
]
+ SOverlay : : Slot ( )
2014-03-14 14:13:41 -04:00
[
SAssignNew ( InnerVerticalBox , SVerticalBox )
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. HAlign ( HAlign_Fill )
. VAlign ( VAlign_Top )
2014-09-15 21:48:46 -04:00
. Padding ( Settings - > GetNonPinNodeBodyPadding ( ) )
2014-03-14 14:13:41 -04:00
[
TitleAreaWidget
]
2014-09-15 21:48:46 -04:00
2014-03-14 14:13:41 -04:00
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. HAlign ( HAlign_Fill )
. VAlign ( VAlign_Top )
[
CreateNodeContentArea ( )
]
2014-09-15 21:48:46 -04:00
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( Settings - > GetNonPinNodeBodyPadding ( ) )
[
ErrorText - > AsShared ( )
]
2014-03-14 14:13:41 -04:00
]
]
] ;
2014-11-03 10:40:57 -05:00
// Create comment bubble
TSharedPtr < SCommentBubble > CommentBubble ;
SAssignNew ( CommentBubble , SCommentBubble )
. GraphNode ( GraphNode )
. Text ( this , & SGraphNode : : GetNodeComment )
. ColorAndOpacity ( this , & SGraphNode : : GetCommentColor )
. AllowPinning ( true )
. EnableTitleBarBubble ( true )
. EnableBubbleCtrls ( true )
. GraphLOD ( this , & SGraphNode : : GetCurrentLOD )
. IsGraphNodeHovered ( this , & SGraphNode : : IsHovered ) ;
GetOrAddSlot ( ENodeZone : : TopCenter )
. SlotOffset ( TAttribute < FVector2D > ( CommentBubble . Get ( ) , & SCommentBubble : : GetOffset ) )
. SlotSize ( TAttribute < FVector2D > ( CommentBubble . Get ( ) , & SCommentBubble : : GetSize ) )
. AllowScaling ( TAttribute < bool > ( CommentBubble . Get ( ) , & SCommentBubble : : IsScalingAllowed ) )
. VAlign ( VAlign_Top )
[
CommentBubble . ToSharedRef ( )
] ;
2014-03-14 14:13:41 -04:00
CreateBelowWidgetControls ( MainVerticalBox ) ;
CreatePinWidgets ( ) ;
2014-04-02 18:09:23 -04:00
CreateInputSideAddButton ( LeftNodeBox ) ;
CreateOutputSideAddButton ( RightNodeBox ) ;
2014-03-14 14:13:41 -04:00
CreateBelowPinControls ( InnerVerticalBox ) ;
CreateAdvancedViewArrow ( InnerVerticalBox ) ;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef < SWidget > SGraphNode : : CreateNodeContentArea ( )
{
// NODE CONTENT AREA
return SNew ( SBorder )
. BorderImage ( FEditorStyle : : GetBrush ( " NoBorder " ) )
. HAlign ( HAlign_Fill )
. VAlign ( VAlign_Fill )
. Padding ( FMargin ( 0 , 3 ) )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. HAlign ( HAlign_Left )
. FillWidth ( 1.0f )
[
// LEFT
SAssignNew ( LeftNodeBox , SVerticalBox )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. HAlign ( HAlign_Right )
[
// RIGHT
SAssignNew ( RightNodeBox , SVerticalBox )
]
] ;
}
/** Returns visibility of AdvancedViewButton */
EVisibility SGraphNode : : AdvancedViewArrowVisibility ( ) const
{
const bool bShowAdvancedViewArrow = GraphNode & & ( ENodeAdvancedPins : : NoPins ! = GraphNode - > AdvancedPinDisplay ) ;
return bShowAdvancedViewArrow ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
2014-12-10 14:24:09 -05:00
void SGraphNode : : OnAdvancedViewChanged ( const ECheckBoxState NewCheckedState )
2014-03-14 14:13:41 -04:00
{
if ( GraphNode & & ( ENodeAdvancedPins : : NoPins ! = GraphNode - > AdvancedPinDisplay ) )
{
2014-12-10 14:24:09 -05:00
const bool bAdvancedPinsHidden = ( NewCheckedState ! = ECheckBoxState : : Checked ) ;
2014-03-14 14:13:41 -04:00
GraphNode - > AdvancedPinDisplay = bAdvancedPinsHidden ? ENodeAdvancedPins : : Hidden : ENodeAdvancedPins : : Shown ;
}
}
2014-12-10 14:24:09 -05:00
ECheckBoxState SGraphNode : : IsAdvancedViewChecked ( ) const
2014-03-14 14:13:41 -04:00
{
const bool bAdvancedPinsHidden = GraphNode & & ( ENodeAdvancedPins : : Hidden = = GraphNode - > AdvancedPinDisplay ) ;
2014-12-10 14:24:09 -05:00
return bAdvancedPinsHidden ? ECheckBoxState : : Unchecked : ECheckBoxState : : Checked ;
2014-03-14 14:13:41 -04:00
}
const FSlateBrush * SGraphNode : : GetAdvancedViewArrow ( ) const
{
const bool bAdvancedPinsHidden = GraphNode & & ( ENodeAdvancedPins : : Hidden = = GraphNode - > AdvancedPinDisplay ) ;
return FEditorStyle : : GetBrush ( bAdvancedPinsHidden ? TEXT ( " Kismet.TitleBarEditor.ArrowDown " ) : TEXT ( " Kismet.TitleBarEditor.ArrowUp " ) ) ;
}
/** Create widget to show/hide advanced pins */
void SGraphNode : : CreateAdvancedViewArrow ( TSharedPtr < SVerticalBox > MainBox )
{
const bool bHidePins = OwnerGraphPanelPtr . IsValid ( ) & & ( OwnerGraphPanelPtr . Pin ( ) - > GetPinVisibility ( ) ! = SGraphEditor : : Pin_Show ) ;
const bool bAnyAdvancedPin = GraphNode & & ( ENodeAdvancedPins : : NoPins ! = GraphNode - > AdvancedPinDisplay ) ;
if ( ! bHidePins & & GraphNode & & MainBox . IsValid ( ) )
{
MainBox - > AddSlot ( )
. AutoHeight ( )
. HAlign ( HAlign_Fill )
. VAlign ( VAlign_Top )
. Padding ( 3 , 0 , 3 , 3 )
[
SNew ( SCheckBox )
. Visibility ( this , & SGraphNode : : AdvancedViewArrowVisibility )
. OnCheckStateChanged ( this , & SGraphNode : : OnAdvancedViewChanged )
. IsChecked ( this , & SGraphNode : : IsAdvancedViewChecked )
. Cursor ( EMouseCursor : : Default )
. Style ( FEditorStyle : : Get ( ) , " Graph.Node.AdvancedView " )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. VAlign ( VAlign_Center )
. HAlign ( HAlign_Center )
[
SNew ( SImage )
. Image ( this , & SGraphNode : : GetAdvancedViewArrow )
]
]
] ;
}
}
void SGraphNode : : CreateStandardPinWidget ( UEdGraphPin * CurPin )
{
const UEdGraphSchema_K2 * K2Schema = Cast < const UEdGraphSchema_K2 > ( GraphNode - > GetSchema ( ) ) ;
bool bHideNoConnectionPins = false ;
bool bHideNoConnectionNoDefaultPins = false ;
// Not allowed to hide exec pins
const bool bCanHidePin = ( K2Schema & & ( CurPin - > PinType . PinCategory ! = K2Schema - > PC_Exec ) ) ;
if ( OwnerGraphPanelPtr . IsValid ( ) & & bCanHidePin )
{
bHideNoConnectionPins = OwnerGraphPanelPtr . Pin ( ) - > GetPinVisibility ( ) = = SGraphEditor : : Pin_HideNoConnection ;
bHideNoConnectionNoDefaultPins = OwnerGraphPanelPtr . Pin ( ) - > GetPinVisibility ( ) = = SGraphEditor : : Pin_HideNoConnectionNoDefault ;
}
const bool bIsOutputPin = CurPin - > Direction = = EGPD_Output ;
const bool bPinHasDefaultValue = ! CurPin - > DefaultValue . IsEmpty ( ) | | ( CurPin - > DefaultObject ! = NULL ) ;
const bool bIsSelfTarget = K2Schema & & ( CurPin - > PinType . PinCategory = = K2Schema - > PC_Object ) & & ( CurPin - > PinName = = K2Schema - > PN_Self ) ;
const bool bPinHasValidDefault = ! bIsOutputPin & & ( bPinHasDefaultValue | | bIsSelfTarget ) ;
const bool bPinHasConections = CurPin - > LinkedTo . Num ( ) > 0 ;
const bool bPinDesiresToBeHidden = CurPin - > bHidden | | ( bHideNoConnectionPins & & ! bPinHasConections ) | | ( bHideNoConnectionNoDefaultPins & & ! bPinHasConections & & ! bPinHasValidDefault ) ;
// No matter how strong the desire, a pin with connections can never be hidden!
const bool bShowPin = ! bPinDesiresToBeHidden | | bPinHasConections ;
if ( bShowPin )
{
TSharedPtr < SGraphPin > NewPin = CreatePinWidget ( CurPin ) ;
check ( NewPin . IsValid ( ) ) ;
NewPin - > SetIsEditable ( IsEditable ) ;
this - > AddPin ( NewPin . ToSharedRef ( ) ) ;
}
}
void SGraphNode : : CreatePinWidgets ( )
{
// Create Pin widgets for each of the pins.
for ( int32 PinIndex = 0 ; PinIndex < GraphNode - > Pins . Num ( ) ; + + PinIndex )
{
UEdGraphPin * CurPin = GraphNode - > Pins [ PinIndex ] ;
CreateStandardPinWidget ( CurPin ) ;
}
}
TSharedPtr < SGraphPin > SGraphNode : : CreatePinWidget ( UEdGraphPin * Pin ) const
{
return FNodeFactory : : CreatePinWidget ( Pin ) ;
}
2014-09-15 19:53:19 -04:00
void SGraphNode : : AddPin ( const TSharedRef < SGraphPin > & PinToAdd )
2014-03-14 14:13:41 -04:00
{
2014-09-15 19:53:19 -04:00
PinToAdd - > SetOwner ( SharedThis ( this ) ) ;
2014-03-14 14:13:41 -04:00
const UEdGraphPin * PinObj = PinToAdd - > GetPinObj ( ) ;
2014-09-15 21:48:46 -04:00
const bool bAdvancedParameter = ( PinObj ! = nullptr ) & & PinObj - > bAdvancedView ;
if ( bAdvancedParameter )
2014-03-14 14:13:41 -04:00
{
PinToAdd - > SetVisibility ( TAttribute < EVisibility > ( PinToAdd , & SGraphPin : : IsPinVisibleAsAdvanced ) ) ;
}
2014-09-15 21:48:46 -04:00
2014-03-14 14:13:41 -04:00
if ( PinToAdd - > GetDirection ( ) = = EEdGraphPinDirection : : EGPD_Input )
{
LeftNodeBox - > AddSlot ( )
. AutoHeight ( )
. HAlign ( HAlign_Left )
. VAlign ( VAlign_Center )
2014-09-15 21:48:46 -04:00
. Padding ( Settings - > GetInputPinPadding ( ) )
2014-03-14 14:13:41 -04:00
[
PinToAdd
] ;
InputPins . Add ( PinToAdd ) ;
}
else // Direction == EEdGraphPinDirection::EGPD_Output
{
RightNodeBox - > AddSlot ( )
. AutoHeight ( )
. HAlign ( HAlign_Right )
. VAlign ( VAlign_Center )
2014-09-15 21:48:46 -04:00
. Padding ( Settings - > GetOutputPinPadding ( ) )
2014-03-14 14:13:41 -04:00
[
PinToAdd
] ;
OutputPins . Add ( PinToAdd ) ;
}
}
/**
* Get all the pins found on this node .
*
* @ param AllPins The set of pins found on this node .
*/
void SGraphNode : : GetPins ( TSet < TSharedRef < SWidget > > & AllPins ) const
{
for ( int32 PinIndex = 0 ; PinIndex < this - > InputPins . Num ( ) ; + + PinIndex )
{
AllPins . Add ( InputPins [ PinIndex ] ) ;
}
for ( int32 PinIndex = 0 ; PinIndex < this - > OutputPins . Num ( ) ; + + PinIndex )
{
AllPins . Add ( OutputPins [ PinIndex ] ) ;
}
}
void SGraphNode : : GetPins ( TArray < TSharedRef < SWidget > > & AllPins ) const
{
for ( int32 PinIndex = 0 ; PinIndex < this - > InputPins . Num ( ) ; + + PinIndex )
{
AllPins . Add ( InputPins [ PinIndex ] ) ;
}
for ( int32 PinIndex = 0 ; PinIndex < this - > OutputPins . Num ( ) ; + + PinIndex )
{
AllPins . Add ( OutputPins [ PinIndex ] ) ;
}
}
TSharedPtr < SGraphPin > SGraphNode : : GetHoveredPin ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent ) const
{
// We just need to find the one WidgetToFind among our descendants.
TSet < TSharedRef < SWidget > > MyPins ;
{
GetPins ( MyPins ) ;
}
TMap < TSharedRef < SWidget > , FArrangedWidget > Result ;
FindChildGeometries ( MyGeometry , MyPins , Result ) ;
if ( Result . Num ( ) > 0 )
{
FArrangedChildren ArrangedPins ( EVisibility : : Visible ) ;
Result . GenerateValueArray ( ArrangedPins . GetInternalArray ( ) ) ;
int32 HoveredPinIndex = SWidget : : FindChildUnderMouse ( ArrangedPins , MouseEvent ) ;
if ( HoveredPinIndex ! = INDEX_NONE )
{
2014-08-25 12:51:49 -04:00
return StaticCastSharedRef < SGraphPin > ( ArrangedPins [ HoveredPinIndex ] . Widget ) ;
2014-03-14 14:13:41 -04:00
}
}
return TSharedPtr < SGraphPin > ( ) ;
}
TSharedPtr < SGraphPin > SGraphNode : : FindWidgetForPin ( UEdGraphPin * ThePin ) const
{
// Search input or output pins?
const TArray < TSharedRef < SGraphPin > > & PinsToSearch = ( ThePin - > Direction = = EGPD_Input ) ? InputPins : OutputPins ;
// Actually search for the widget
for ( int32 PinIndex = 0 ; PinIndex < PinsToSearch . Num ( ) ; + + PinIndex )
{
if ( PinsToSearch [ PinIndex ] - > GetPinObj ( ) = = ThePin )
{
return PinsToSearch [ PinIndex ] ;
}
}
return TSharedPtr < SGraphPin > ( NULL ) ;
}
void SGraphNode : : PlaySpawnEffect ( )
{
---- 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
SpawnAnim . Play ( this - > AsShared ( ) ) ;
2014-03-14 14:13:41 -04:00
}
FVector2D SGraphNode : : GetContentScale ( ) const
{
const float CurZoomValue = ZoomCurve . GetLerp ( ) ;
return FVector2D ( CurZoomValue , CurZoomValue ) ;
}
FLinearColor SGraphNode : : GetColorAndOpacity ( ) const
{
return FLinearColor ( 1 , 1 , 1 , FadeCurve . GetLerp ( ) ) ;
}
FLinearColor SGraphNode : : GetPinLabelColorAndOpacity ( ) const
{
return FLinearColor ( 0 , 0 , 0 , FadeCurve . GetLerp ( ) ) ;
}
SGraphNode : : SGraphNode ( )
: bProvidedComplexTooltip ( false )
, bRenameIsPending ( false )
2014-07-30 14:51:27 -04:00
, ErrorColor ( FLinearColor : : White )
2014-03-14 14:13:41 -04:00
, CachedUnscaledPosition ( FVector2D : : ZeroVector )
2014-09-15 21:48:46 -04:00
, Settings ( GetDefault < UGraphEditorSettings > ( ) )
2014-03-14 14:13:41 -04:00
{
// Set up animation
{
ZoomCurve = SpawnAnim . AddCurve ( 0 , 0.1f ) ;
FadeCurve = SpawnAnim . AddCurve ( 0.15f , 0.15f ) ;
SpawnAnim . JumpToEnd ( ) ;
}
}
void SGraphNode : : PositionThisNodeBetweenOtherNodes ( const TMap < UObject * , TSharedRef < SNode > > & NodeToWidgetLookup , UEdGraphNode * PreviousNode , UEdGraphNode * NextNode , float HeightAboveWire ) const
{
if ( ( PreviousNode ! = NULL ) & & ( NextNode ! = NULL ) )
{
TSet < UEdGraphNode * > PrevNodes ;
PrevNodes . Add ( PreviousNode ) ;
TSet < UEdGraphNode * > NextNodes ;
NextNodes . Add ( NextNode ) ;
PositionThisNodeBetweenOtherNodes ( NodeToWidgetLookup , PrevNodes , NextNodes , HeightAboveWire ) ;
}
}
void SGraphNode : : PositionThisNodeBetweenOtherNodes ( const TMap < UObject * , TSharedRef < SNode > > & NodeToWidgetLookup , TSet < UEdGraphNode * > & PreviousNodes , TSet < UEdGraphNode * > & NextNodes , float HeightAboveWire ) const
{
// Find the previous position centroid
FVector2D PrevPos ( 0.0f , 0.0f ) ;
for ( auto NodeIt = PreviousNodes . CreateConstIterator ( ) ; NodeIt ; + + NodeIt )
{
UEdGraphNode * PreviousNode = * NodeIt ;
const FVector2D CornerPos ( PreviousNode - > NodePosX , PreviousNode - > NodePosY ) ;
PrevPos + = CornerPos + NodeToWidgetLookup . FindChecked ( PreviousNode ) - > GetDesiredSize ( ) * 0.5f ;
}
// Find the next position centroid
FVector2D NextPos ( 0.0f , 0.0f ) ;
for ( auto NodeIt = NextNodes . CreateConstIterator ( ) ; NodeIt ; + + NodeIt )
{
UEdGraphNode * NextNode = * NodeIt ;
const FVector2D CornerPos ( NextNode - > NodePosX , NextNode - > NodePosY ) ;
NextPos + = CornerPos + NodeToWidgetLookup . FindChecked ( NextNode ) - > GetDesiredSize ( ) * 0.5f ;
}
PositionThisNodeBetweenOtherNodes ( PrevPos , NextPos , HeightAboveWire ) ;
}
void SGraphNode : : PositionThisNodeBetweenOtherNodes ( const FVector2D & PrevPos , const FVector2D & NextPos , float HeightAboveWire ) const
{
const FVector2D DesiredSize = GetDesiredSize ( ) ;
FVector2D DeltaPos ( NextPos - PrevPos ) ;
if ( DeltaPos . IsNearlyZero ( ) )
{
DeltaPos = FVector2D ( 10.0f , 0.0f ) ;
}
2014-11-26 10:01:12 -05:00
const FVector2D Normal = FVector2D ( DeltaPos . Y , - DeltaPos . X ) . GetSafeNormal ( ) ;
2014-03-14 14:13:41 -04:00
const FVector2D SlidingCapsuleBias = FVector2D : : ZeroVector ; //(0.5f * FMath::Sin(Normal.X * (float)HALF_PI) * DesiredSize.X, 0.0f);
const FVector2D NewCenter = PrevPos + ( 0.5f * DeltaPos ) + ( HeightAboveWire * Normal ) + SlidingCapsuleBias ;
// Now we need to adjust the new center by the node size and zoom factor
const FVector2D NewCorner = NewCenter - ( 0.5f * DesiredSize ) ;
GraphNode - > NodePosX = NewCorner . X ;
GraphNode - > NodePosY = NewCorner . Y ;
}
2015-01-08 11:35:01 -05:00
FText SGraphNode : : GetErrorMsgToolTip ( ) const
2014-03-14 14:13:41 -04:00
{
2015-01-08 11:35:01 -05:00
return FText : : FromString ( GraphNode - > ErrorMsg ) ;
2014-03-14 14:13:41 -04:00
}
bool SGraphNode : : IsNameReadOnly ( ) const
{
return ! GraphNode - > bCanRenameNode ;
}
2014-05-15 17:34:14 -04:00
bool SGraphNode : : OnVerifyNameTextChanged ( const FText & InText , FText & OutErrorMessage )
2014-03-14 14:13:41 -04:00
{
bool bValid ( true ) ;
2014-05-15 17:34:14 -04:00
if ( ( GetEditableNodeTitle ( ) ! = InText . ToString ( ) ) & & OnVerifyTextCommit . IsBound ( ) )
2014-03-14 14:13:41 -04:00
{
bValid = OnVerifyTextCommit . Execute ( InText , GraphNode ) ;
}
OutErrorMessage = FText : : FromString ( TEXT ( " Error " ) ) ;
//UpdateErrorInfo();
//ErrorReporting->SetError(ErrorMsg);
return bValid ;
}
2014-05-15 17:34:14 -04:00
void SGraphNode : : OnNameTextCommited ( const FText & InText , ETextCommit : : Type CommitInfo )
2014-03-14 14:13:41 -04:00
{
OnTextCommitted . ExecuteIfBound ( InText , CommitInfo , GraphNode ) ;
UpdateErrorInfo ( ) ;
2014-05-15 17:34:14 -04:00
if ( ErrorReporting . IsValid ( ) )
{
ErrorReporting - > SetError ( ErrorMsg ) ;
}
2014-03-14 14:13:41 -04:00
}
void SGraphNode : : RequestRename ( )
{
if ( ( GraphNode ! = NULL ) & & GraphNode - > bCanRenameNode )
{
bRenameIsPending = true ;
}
}
void SGraphNode : : ApplyRename ( )
{
if ( bRenameIsPending )
{
bRenameIsPending = false ;
InlineEditableText - > EnterEditingMode ( ) ;
}
}
FSlateRect SGraphNode : : GetTitleRect ( ) const
{
const FVector2D NodePosition = GetPosition ( ) ;
const FVector2D NodeSize = GraphNode ? InlineEditableText - > GetDesiredSize ( ) : GetDesiredSize ( ) ;
return FSlateRect ( NodePosition . X , NodePosition . Y + NodeSize . Y , NodePosition . X + NodeSize . X , NodePosition . Y ) ;
}
void SGraphNode : : NotifyDisallowedPinConnection ( const UEdGraphPin * PinA , const UEdGraphPin * PinB ) const
{
OnDisallowedPinConnection . ExecuteIfBound ( PinA , PinB ) ;
}
bool SGraphNode : : UseLowDetailNodeTitles ( ) const
{
if ( const SGraphPanel * MyOwnerPanel = GetOwnerPanel ( ) . Get ( ) )
{
return ( MyOwnerPanel - > GetCurrentLOD ( ) < = EGraphRenderingLOD : : LowestDetail ) & & ! InlineEditableText - > IsInEditMode ( ) ;
}
else
{
return false ;
}
}
2014-04-02 18:09:23 -04:00
TSharedRef < SWidget > SGraphNode : : AddPinButtonContent ( FText PinText , FText PinTooltipText , bool bRightSide , FString DocumentationExcerpt , TSharedPtr < SToolTip > CustomTooltip )
{
TSharedPtr < SWidget > ButtonContent ;
if ( bRightSide )
{
SAssignNew ( ButtonContent , SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. HAlign ( HAlign_Left )
[
SNew ( STextBlock )
. Text ( PinText )
. ColorAndOpacity ( FLinearColor : : White )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
. Padding ( 7 , 0 , 0 , 0 )
[
SNew ( SImage )
. Image ( FEditorStyle : : GetBrush ( TEXT ( " PropertyWindow.Button_AddToArray " ) ) )
] ;
}
else
{
SAssignNew ( ButtonContent , SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
. Padding ( 0 , 0 , 7 , 0 )
[
SNew ( SImage )
. Image ( FEditorStyle : : GetBrush ( TEXT ( " PropertyWindow.Button_AddToArray " ) ) )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. HAlign ( HAlign_Left )
[
SNew ( STextBlock )
. Text ( PinText )
. ColorAndOpacity ( FLinearColor : : White )
] ;
}
TSharedPtr < SToolTip > Tooltip ;
if ( CustomTooltip . IsValid ( ) )
{
Tooltip = CustomTooltip ;
}
else if ( ! DocumentationExcerpt . IsEmpty ( ) )
{
Tooltip = IDocumentation : : Get ( ) - > CreateToolTip ( PinTooltipText , NULL , GraphNode - > GetDocumentationLink ( ) , DocumentationExcerpt ) ;
}
TSharedRef < SButton > AddPinButton = SNew ( SButton )
. ContentPadding ( 0.0f )
. ButtonStyle ( FEditorStyle : : Get ( ) , " NoBorder " )
. OnClicked ( this , & SGraphNode : : OnAddPin )
. ToolTipText ( PinTooltipText )
. ToolTip ( Tooltip )
. Visibility ( this , & SGraphNode : : IsAddPinButtonVisible )
[
ButtonContent . ToSharedRef ( )
] ;
AddPinButton - > SetCursor ( EMouseCursor : : Hand ) ;
return AddPinButton ;
}
EVisibility SGraphNode : : IsAddPinButtonVisible ( ) const
{
bool bIsHidden = false ;
auto OwnerGraphPanel = OwnerGraphPanelPtr . Pin ( ) ;
if ( OwnerGraphPanel . IsValid ( ) )
{
bIsHidden | = ( SGraphEditor : : EPinVisibility : : Pin_Show ! = OwnerGraphPanel - > GetPinVisibility ( ) ) ;
bIsHidden | = ( OwnerGraphPanel - > GetCurrentLOD ( ) < = EGraphRenderingLOD : : LowDetail ) ;
}
return bIsHidden ? EVisibility : : Collapsed : EVisibility : : Visible ;
}
2014-09-05 07:39:52 -04:00
void SGraphNode : : PopulateMetaTag ( FGraphNodeMetaData * TagMeta ) const
{
if ( GraphNode ! = nullptr )
{
// We want the name of the blueprint as our name - we can find the node from the GUID
UObject * Package = GraphNode - > GetOutermost ( ) ;
UObject * LastOuter = GraphNode - > GetOuter ( ) ;
while ( LastOuter - > GetOuter ( ) ! = Package )
{
LastOuter = LastOuter - > GetOuter ( ) ;
}
TagMeta - > Tag = FName ( * FString : : Printf ( TEXT ( " GraphNode_%s_%s " ) , * LastOuter - > GetFullName ( ) , * GraphNode - > NodeGuid . ToString ( ) ) ) ;
TagMeta - > OuterName = LastOuter - > GetFullName ( ) ;
TagMeta - > GUID = GraphNode - > NodeGuid ;
TagMeta - > FriendlyName = FString : : Printf ( TEXT ( " %s in %s " ) , * GraphNode - > GetNodeTitle ( ENodeTitleType : : ListView ) . ToString ( ) , * TagMeta - > OuterName ) ;
}
2014-11-03 10:40:57 -05:00
}
EGraphRenderingLOD : : Type SGraphNode : : GetCurrentLOD ( ) const
{
return OwnerGraphPanelPtr . IsValid ( ) ? OwnerGraphPanelPtr . Pin ( ) - > GetCurrentLOD ( ) : EGraphRenderingLOD : : DefaultDetail ;
2014-09-05 07:39:52 -04:00
}