// Copyright Epic Games, Inc. All Rights Reserved. #include "KismetNodes/SGraphNodeK2Composite.h" #include "Containers/Array.h" #include "Delegates/Delegate.h" #include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphNode.h" #include "Fonts/SlateFontInfo.h" #include "Framework/Application/SlateApplication.h" #include "GenericPlatform/GenericApplication.h" #include "GenericPlatform/ICursor.h" #include "GraphEditorSettings.h" #include "HAL/PlatformCrt.h" #include "IDocumentation.h" #include "IDocumentationPage.h" #include "Internationalization/Internationalization.h" #include "K2Node_Composite.h" #include "Layout/Margin.h" #include "Math/Vector2D.h" #include "Misc/Attribute.h" #include "Misc/Optional.h" #include "SCommentBubble.h" #include "SGraphNode.h" #include "SGraphPreviewer.h" #include "SNodePanel.h" #include "SlotBase.h" #include "Styling/AppStyle.h" #include "Styling/CoreStyle.h" #include "Styling/SlateColor.h" #include "Templates/Casts.h" #include "Types/SlateEnums.h" #include "UObject/ObjectPtr.h" #include "UObject/UObjectGlobals.h" #include "Widgets/Images/SImage.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SSpacer.h" #include "Widgets/Notifications/SErrorText.h" #include "Widgets/SBoxPanel.h" #include "Widgets/SOverlay.h" #include "Widgets/SToolTip.h" #include "Widgets/Text/SInlineEditableTextBlock.h" #include "Widgets/Text/STextBlock.h" class SWidget; ///////////////////////////////////////////////////// // SGraphNodeK2Composite void SGraphNodeK2Composite::Construct(const FArguments& InArgs, UK2Node_Composite* InNode) { GraphNode = InNode; SetCursor(EMouseCursor::CardinalCross); UpdateGraphNode(); } void SGraphNodeK2Composite::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(); SetupErrorReporting(); TSharedPtr NodeTitle = SNew(SNodeTitle, GraphNode); // // ______________________ // | TITLE AREA | // +-------+------+-------+ // | (>) L | | R (>) | // | (>) E | | I (>) | // | (>) F | | G (>) | // | (>) T | | H (>) | // | | | T (>) | // |_______|______|_______| // this->ContentScale.Bind( this, &SGraphNode::GetContentScale ); this->GetOrAddSlot( ENodeZone::Center ) .HAlign(HAlign_Center) .VAlign(VAlign_Center) [ SNew(SBorder) .BorderImage( FAppStyle::GetBrush( "Graph.CollapsedNode.Body" ) ) .Padding(0.0f) [ SNew(SOverlay) +SOverlay::Slot() [ SNew(SImage) .Image( FAppStyle::GetBrush("Graph.CollapsedNode.BodyColorSpill") ) .ColorAndOpacity( this, &SGraphNode::GetNodeTitleColor ) ] +SOverlay::Slot() [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() .HAlign(HAlign_Fill) .VAlign(VAlign_Top) [ SNew(SOverlay) +SOverlay::Slot() .HAlign(HAlign_Left) .VAlign(VAlign_Center) [ SNew(SBorder) .BorderImage( FAppStyle::GetBrush("NoBorder") ) // Graph.CollapsedNode.ColorSpill .Padding( FMargin(10.0f,5.0f,30.0f,3.0f) ) [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() .HAlign(HAlign_Fill) .VAlign(VAlign_Top) [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ SAssignNew(InlineEditableText, SInlineEditableTextBlock) .Style( FAppStyle::Get(), "Graph.Node.NodeTitleInlineEditableText" ) .Text( NodeTitle.Get(), &SNodeTitle::GetHeadTitle ) .OnVerifyTextChanged(this, &SGraphNodeK2Composite::OnVerifyNameTextChanged) .OnTextCommitted(this, &SGraphNodeK2Composite::OnNameTextCommited) .IsReadOnly( this, &SGraphNodeK2Composite::IsNameReadOnly ) .IsSelected(this, &SGraphNodeK2Composite::IsSelectedExclusively) ] +SVerticalBox::Slot() .AutoHeight() [ NodeTitle.ToSharedRef() ] ] ] ] ] +SVerticalBox::Slot() .AutoHeight() .HAlign(HAlign_Fill) .VAlign(VAlign_Top) [ CreateNodeBody() ] +SVerticalBox::Slot() .AutoHeight() .Padding(1.0f) [ ErrorReporting->AsWidget() ] ] ] ]; // Create comment bubble TSharedPtr CommentBubble; const FSlateColor CommentColor = GetDefault()->DefaultCommentNodeTitleColor; SAssignNew( CommentBubble, SCommentBubble ) .GraphNode( GraphNode ) .Text( this, &SGraphNode::GetNodeComment ) .OnTextCommitted( this, &SGraphNode::OnCommentTextCommitted ) .ColorAndOpacity( CommentColor ) .AllowPinning( true ) .EnableTitleBarBubble( true ) .EnableBubbleCtrls( true ) .GraphLOD( this, &SGraphNode::GetCurrentLOD ) .IsGraphNodeHovered( this, &SGraphNode::IsHovered ); GetOrAddSlot( ENodeZone::TopCenter ) .SlotOffset2f( TAttribute( CommentBubble.Get(), &SCommentBubble::GetOffset2f )) .SlotSize2f( TAttribute( CommentBubble.Get(), &SCommentBubble::GetSize2f )) .AllowScaling( TAttribute( CommentBubble.Get(), &SCommentBubble::IsScalingAllowed )) .VAlign( VAlign_Top ) [ CommentBubble.ToSharedRef() ]; CreatePinWidgets(); } UEdGraph* SGraphNodeK2Composite::GetInnerGraph() const { UK2Node_Composite* CompositeNode = CastChecked(GraphNode); return CompositeNode->BoundGraph; } TSharedPtr SGraphNodeK2Composite::GetComplexTooltip() { if (UEdGraph* BoundGraph = GetInnerGraph()) { struct LocalUtils { static bool IsInteractive() { const FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys(); return ( ModifierKeys.IsAltDown() && ModifierKeys.IsControlDown() ); } }; TSharedPtr FinalToolTip = NULL; TSharedPtr Container = NULL; SAssignNew(FinalToolTip, SToolTip) .IsInteractive_Static(&LocalUtils::IsInteractive) [ SAssignNew(Container, SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ SNew( STextBlock ) .Text(this, &SGraphNodeK2Composite::GetTooltipTextForNode) .Font(FCoreStyle::GetDefaultFontStyle("Regular", 8)) .WrapTextAt(160.0f) ] +SVerticalBox::Slot() .AutoHeight() [ // Create preview for the tooltip, make sure to disable state overlays to prevent // PIE and read-only borders obscuring the graph SNew(SGraphPreviewer, BoundGraph) .CornerOverlayText(this, &SGraphNodeK2Composite::GetPreviewCornerText) .ShowGraphStateOverlay(false) ] ]; // Check to see whether this node has a documentation excerpt. If it does, create a doc box for the tooltip TSharedRef DocPage = IDocumentation::Get()->GetPage(GraphNode->GetDocumentationLink(), NULL); if(DocPage->HasExcerpt(GraphNode->GetDocumentationExcerptName())) { Container->AddSlot() .AutoHeight() .Padding(FMargin( 0.0f, 5.0f )) [ IDocumentation::Get()->CreateToolTip(FText::FromString("Documentation"), NULL, GraphNode->GetDocumentationLink(), GraphNode->GetDocumentationExcerptName()) ]; } return FinalToolTip; } else { return SNew(SToolTip) [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ SNew( STextBlock ) .Text(NSLOCTEXT("CompositeNode", "CompositeNodeInvalidGraphMessage", "ERROR: Invalid Graph")) .Font(FCoreStyle::GetDefaultFontStyle("Regular", 8)) .WrapTextAt(160.0f) ] ]; } } FText SGraphNodeK2Composite::GetPreviewCornerText() const { UEdGraph* BoundGraph = GetInnerGraph(); return FText::FromString(BoundGraph->GetName()); } FText SGraphNodeK2Composite::GetTooltipTextForNode() const { return GraphNode->GetTooltipText(); } TSharedRef SGraphNodeK2Composite::CreateNodeBody() { if( GraphNode && GraphNode->Pins.Num() > 0 ) { // Create the input and output pin areas if there are pins return SNew(SBorder) .BorderImage( FAppStyle::GetBrush("NoBorder") ) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .Padding( FMargin(0.0f,3.0f) ) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .HAlign(HAlign_Left) .FillWidth(1.0f) [ // LEFT SAssignNew(LeftNodeBox, SVerticalBox) ] +SHorizontalBox::Slot() .AutoWidth() .HAlign(HAlign_Right) [ // RIGHT SAssignNew(RightNodeBox, SVerticalBox) ] ]; } else { // Create a spacer so the node has some body to it return SNew(SSpacer) .Size(FVector2D(100.f, 50.f)); } }