int32 SPaperEditorViewport::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const { int32 MaxLayerId = SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); ++MaxLayerId; PaintSoftwareCursor(AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); return MaxLayerId; }
int32 SGraphPanel::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { #if SLATE_HD_STATS SCOPE_CYCLE_COUNTER( STAT_SlateOnPaint_SGraphPanel ); #endif CachedAllottedGeometryScaledSize = AllottedGeometry.Size * AllottedGeometry.Scale; //Style used for objects that are the same between revisions FWidgetStyle FadedStyle = InWidgetStyle; FadedStyle.BlendColorAndOpacityTint(FLinearColor(0.45f,0.45f,0.45f,0.45f)); // First paint the background const UEditorExperimentalSettings& Options = *GetDefault<UEditorExperimentalSettings>(); const FSlateBrush* BackgroundImage = FEditorStyle::GetBrush(TEXT("Graph.Panel.SolidBackground")); PaintBackgroundAsLines(BackgroundImage, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId); const float ZoomFactor = AllottedGeometry.Scale * GetZoomAmount(); FArrangedChildren ArrangedChildren(EVisibility::Visible); ArrangeChildNodes(AllottedGeometry, ArrangedChildren); // Determine some 'global' settings based on current LOD const bool bDrawShadowsThisFrame = GetCurrentLOD() > EGraphRenderingLOD::LowestDetail; // Because we paint multiple children, we must track the maximum layer id that they produced in case one of our parents // wants to an overlay for all of its contents. // Save LayerId for comment boxes to ensure they always appear below nodes & wires const int32 CommentNodeShadowLayerId = LayerId++; const int32 CommentNodeLayerId = LayerId++; // Save a LayerId for wires, which appear below nodes but above comments // We will draw them later, along with the arrows which appear above nodes. const int32 WireLayerId = LayerId++; const int32 NodeShadowsLayerId = LayerId; const int32 NodeLayerId = NodeShadowsLayerId + 1; int32 MaxLayerId = NodeLayerId; const FVector2D NodeShadowSize = GetDefault<UGraphEditorSettings>()->GetShadowDeltaSize(); const UEdGraphSchema* Schema = GraphObj->GetSchema(); // Draw the child nodes { // When drawing a marquee, need a preview of what the selection will be. const FGraphPanelSelectionSet* SelectionToVisualize = &(SelectionManager.SelectedNodes); FGraphPanelSelectionSet SelectionPreview; if ( Marquee.IsValid() ) { ApplyMarqueeSelection(Marquee, SelectionManager.SelectedNodes, SelectionPreview); SelectionToVisualize = &SelectionPreview; } // Context for rendering node infos FKismetNodeInfoContext Context(GraphObj); TArray<FGraphDiffControl::FNodeMatch> NodeMatches; for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex) { FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex]; TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(CurWidget.Widget); // Examine node to see what layers we should be drawing in int32 ShadowLayerId = NodeShadowsLayerId; int32 ChildLayerId = NodeLayerId; // If a comment node, draw in the dedicated comment slots { UObject* NodeObj = ChildNode->GetObjectBeingDisplayed(); if (NodeObj && NodeObj->IsA(UEdGraphNode_Comment::StaticClass())) { ShadowLayerId = CommentNodeShadowLayerId; ChildLayerId = CommentNodeLayerId; } } const bool bNodeIsVisible = FSlateRect::DoRectanglesIntersect( CurWidget.Geometry.GetClippingRect(), MyClippingRect ); if (bNodeIsVisible) { const bool bSelected = SelectionToVisualize->Contains( StaticCastSharedRef<SNodePanel::SNode>(CurWidget.Widget)->GetObjectBeingDisplayed() ); // Handle Node renaming once the node is visible if( bSelected && ChildNode->IsRenamePending() ) { ChildNode->ApplyRename(); } // Draw the node's shadow. if (bDrawShadowsThisFrame || bSelected) { const FSlateBrush* ShadowBrush = ChildNode->GetShadowBrush(bSelected); FSlateDrawElement::MakeBox( OutDrawElements, ShadowLayerId, CurWidget.Geometry.ToInflatedPaintGeometry(NodeShadowSize), ShadowBrush, MyClippingRect ); } // Draw the comments and information popups for this node, if it has any. { const SNodePanel::SNode::FNodeSlot& CommentSlot = ChildNode->GetOrAddSlot( ENodeZone::TopCenter ); float CommentBubbleY = -CommentSlot.Offset.Get().Y; Context.bSelected = bSelected; TArray<FGraphInformationPopupInfo> Popups; { ChildNode->GetNodeInfoPopups(&Context, /*out*/ Popups); } for (int32 PopupIndex = 0; PopupIndex < Popups.Num(); ++PopupIndex) { FGraphInformationPopupInfo& Popup = Popups[PopupIndex]; PaintComment(Popup.Message, CurWidget.Geometry, MyClippingRect, OutDrawElements, ChildLayerId, Popup.BackgroundColor, /*inout*/ CommentBubbleY, InWidgetStyle); } } int32 CurWidgetsMaxLayerId; { UEdGraphNode* NodeObj = Cast<UEdGraphNode>(ChildNode->GetObjectBeingDisplayed()); /** When diffing nodes, nodes that are different between revisions are opaque, nodes that have not changed are faded */ FGraphDiffControl::FNodeMatch NodeMatch = FGraphDiffControl::FindNodeMatch(GraphObjToDiff, NodeObj, NodeMatches); if (NodeMatch.IsValid()) { NodeMatches.Add(NodeMatch); } const bool bNodeIsDifferent = (!GraphObjToDiff || NodeMatch.Diff()); /* When dragging off a pin, we want to duck the alpha of some nodes */ TSharedPtr< SGraphPin > OnlyStartPin = (1 == PreviewConnectorFromPins.Num()) ? PreviewConnectorFromPins[0].FindInGraphPanel(*this) : TSharedPtr< SGraphPin >(); const bool bNodeIsNotUsableInCurrentContext = Schema->FadeNodeWhenDraggingOffPin(NodeObj, OnlyStartPin.IsValid() ? OnlyStartPin.Get()->GetPinObj() : NULL); const FWidgetStyle& NodeStyleToUse = (bNodeIsDifferent && !bNodeIsNotUsableInCurrentContext)? InWidgetStyle : FadedStyle; // Draw the node.O CurWidgetsMaxLayerId = CurWidget.Widget->Paint( Args.WithNewParent(this), CurWidget.Geometry, MyClippingRect, OutDrawElements, ChildLayerId, NodeStyleToUse, ShouldBeEnabled( bParentEnabled ) ); } // Draw the node's overlay, if it has one. { // Get its size const FVector2D WidgetSize = CurWidget.Geometry.Size; { TArray<FOverlayBrushInfo> OverlayBrushes; ChildNode->GetOverlayBrushes(bSelected, WidgetSize, /*out*/ OverlayBrushes); for (int32 BrushIndex = 0; BrushIndex < OverlayBrushes.Num(); ++BrushIndex) { FOverlayBrushInfo& OverlayInfo = OverlayBrushes[BrushIndex]; const FSlateBrush* OverlayBrush = OverlayInfo.Brush; if(OverlayBrush != NULL) { FPaintGeometry BouncedGeometry = CurWidget.Geometry.ToPaintGeometry(OverlayInfo.OverlayOffset, OverlayBrush->ImageSize, 1.f); // Handle bouncing const float BounceValue = FMath::Sin(2.0f * PI * BounceCurve.GetLerpLooping()); BouncedGeometry.DrawPosition += (OverlayInfo.AnimationEnvelope * BounceValue * ZoomFactor); CurWidgetsMaxLayerId++; FSlateDrawElement::MakeBox( OutDrawElements, CurWidgetsMaxLayerId, BouncedGeometry, OverlayBrush, MyClippingRect ); } } } { TArray<FOverlayWidgetInfo> OverlayWidgets = ChildNode->GetOverlayWidgets(bSelected, WidgetSize); for (int32 WidgetIndex = 0; WidgetIndex < OverlayWidgets.Num(); ++WidgetIndex) { FOverlayWidgetInfo& OverlayInfo = OverlayWidgets[WidgetIndex]; if(OverlayInfo.Widget->GetVisibility() == EVisibility::Visible) { // call SlatePrepass as these widgets are not in the 'normal' child hierarchy OverlayInfo.Widget->SlatePrepass(); const FGeometry WidgetGeometry = CurWidget.Geometry.MakeChild(OverlayInfo.OverlayOffset, OverlayInfo.Widget->GetDesiredSize(), 1.f); OverlayInfo.Widget->Paint(Args.WithNewParent(this), WidgetGeometry, MyClippingRect, OutDrawElements, CurWidgetsMaxLayerId, InWidgetStyle, bParentEnabled); } } } } MaxLayerId = FMath::Max( MaxLayerId, CurWidgetsMaxLayerId + 1 ); } } } MaxLayerId += 1; // Draw connections between pins if (Children.Num() > 0 ) { //@TODO: Pull this into a factory like the pin and node ones FConnectionDrawingPolicy* ConnectionDrawingPolicy; { ConnectionDrawingPolicy = Schema->CreateConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); if (!ConnectionDrawingPolicy) { if (Schema->IsA(UAnimationGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FAnimGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UAnimationStateMachineSchema::StaticClass())) { ConnectionDrawingPolicy = new FStateMachineConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UEdGraphSchema_K2::StaticClass())) { ConnectionDrawingPolicy = new FKismetConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(USoundCueGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FSoundCueGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UMaterialGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FMaterialGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else { ConnectionDrawingPolicy = new FConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements); } } } TArray<TSharedPtr<SGraphPin>> OverridePins; for (const FGraphPinHandle& Handle : PreviewConnectorFromPins) { TSharedPtr<SGraphPin> Pin = Handle.FindInGraphPanel(*this); if (Pin.IsValid()) { OverridePins.Add(Pin); } } ConnectionDrawingPolicy->SetHoveredPins(CurrentHoveredPins, OverridePins, TimeSinceMouseEnteredPin); ConnectionDrawingPolicy->SetMarkedPin(MarkedPin); // Get the set of pins for all children and synthesize geometry for culled out pins so lines can be drawn to them. TMap<TSharedRef<SWidget>, FArrangedWidget> PinGeometries; TSet< TSharedRef<SWidget> > VisiblePins; for (int32 ChildIndex = 0; ChildIndex < Children.Num(); ++ChildIndex) { TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(Children[ChildIndex]); // If this is a culled node, approximate the pin geometry to the corner of the node it is within if (IsNodeCulled(ChildNode, AllottedGeometry)) { TArray< TSharedRef<SWidget> > NodePins; ChildNode->GetPins(NodePins); const FVector2D NodeLoc = ChildNode->GetPosition(); const FGeometry SynthesizedNodeGeometry(GraphCoordToPanelCoord(NodeLoc), AllottedGeometry.AbsolutePosition, FVector2D::ZeroVector, 1.f); for (TArray< TSharedRef<SWidget> >::TConstIterator NodePinIterator(NodePins); NodePinIterator; ++NodePinIterator) { const SGraphPin& PinWidget = static_cast<const SGraphPin&>((*NodePinIterator).Get()); FVector2D PinLoc = NodeLoc + PinWidget.GetNodeOffset(); const FGeometry SynthesizedPinGeometry(GraphCoordToPanelCoord(PinLoc), AllottedGeometry.AbsolutePosition, FVector2D::ZeroVector, 1.f); PinGeometries.Add(*NodePinIterator, FArrangedWidget(*NodePinIterator, SynthesizedPinGeometry)); } // Also add synthesized geometries for culled nodes ArrangedChildren.AddWidget( FArrangedWidget(ChildNode, SynthesizedNodeGeometry) ); } else { ChildNode->GetPins(VisiblePins); } } // Now get the pin geometry for all visible children and append it to the PinGeometries map TMap<TSharedRef<SWidget>, FArrangedWidget> VisiblePinGeometries; { this->FindChildGeometries(AllottedGeometry, VisiblePins, VisiblePinGeometries); PinGeometries.Append(VisiblePinGeometries); } // Draw preview connections (only connected on one end) if (PreviewConnectorFromPins.Num() > 0) { for (const FGraphPinHandle& Handle : PreviewConnectorFromPins) { TSharedPtr< SGraphPin > CurrentStartPin = Handle.FindInGraphPanel(*this); if (!CurrentStartPin.IsValid()) { continue; } const FArrangedWidget* PinGeometry = PinGeometries.Find( CurrentStartPin.ToSharedRef() ); if (PinGeometry != NULL) { FVector2D StartPoint; FVector2D EndPoint; if (CurrentStartPin->GetDirection() == EGPD_Input) { StartPoint = AllottedGeometry.AbsolutePosition + PreviewConnectorEndpoint; EndPoint = FGeometryHelper::VerticalMiddleLeftOf( PinGeometry->Geometry ) - FVector2D(ConnectionDrawingPolicy->ArrowRadius.X, 0); } else { StartPoint = FGeometryHelper::VerticalMiddleRightOf( PinGeometry->Geometry ); EndPoint = AllottedGeometry.AbsolutePosition + PreviewConnectorEndpoint; } ConnectionDrawingPolicy->DrawPreviewConnector(PinGeometry->Geometry, StartPoint, EndPoint, CurrentStartPin.Get()->GetPinObj()); } //@TODO: Re-evaluate this incompatible mojo; it's mutating every pin state every frame to accomplish a visual effect ConnectionDrawingPolicy->SetIncompatiblePinDrawState(CurrentStartPin, VisiblePins); } } else { //@TODO: Re-evaluate this incompatible mojo; it's mutating every pin state every frame to accomplish a visual effect ConnectionDrawingPolicy->ResetIncompatiblePinDrawState(VisiblePins); } // Draw all regular connections ConnectionDrawingPolicy->Draw(PinGeometries, ArrangedChildren); delete ConnectionDrawingPolicy; } // Draw a shadow overlay around the edges of the graph ++MaxLayerId; PaintSurroundSunkenShadow(FEditorStyle::GetBrush(TEXT("Graph.Shadow")), AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); if(ShowGraphStateOverlay.Get()) { const FSlateBrush* BorderBrush = nullptr; if((GEditor->bIsSimulatingInEditor || GEditor->PlayWorld != NULL)) { // Draw a surrounding indicator when PIE is active, to make it clear that the graph is read-only, etc... BorderBrush = FEditorStyle::GetBrush(TEXT("Graph.PlayInEditor")); } else if(!IsEditable.Get()) { // Draw a different border when we're not simulating but the graph is read-only BorderBrush = FEditorStyle::GetBrush(TEXT("Graph.ReadOnlyBorder")); } if(BorderBrush) { // Actually draw the border FSlateDrawElement::MakeBox( OutDrawElements, MaxLayerId, AllottedGeometry.ToPaintGeometry(), BorderBrush, MyClippingRect ); } } // Draw the marquee selection rectangle PaintMarquee(AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); // Draw the software cursor ++MaxLayerId; PaintSoftwareCursor(AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); return MaxLayerId; }
/** SWidget interface */ virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const override { // First paint the background { LayerId = PaintBackground(AllottedGeometry, MyClippingRect, OutDrawElements, LayerId); LayerId++; } FArrangedChildren ArrangedChildren(EVisibility::Visible); ArrangeChildren(AllottedGeometry, ArrangedChildren); // Draw the child nodes // When drawing a marquee, need a preview of what the selection will be. const auto* SelectionToVisualize = &(SelectionManager.SelectedNodes); FGraphPanelSelectionSet SelectionPreview; if (Marquee.IsValid()) { ApplyMarqueeSelection(Marquee, SelectionManager.SelectedNodes, SelectionPreview); SelectionToVisualize = &SelectionPreview; } int32 NodesLayerId = LayerId; for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex) { FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex]; TSharedRef<SWorldTileItem> ChildNode = StaticCastSharedRef<SWorldTileItem>(CurWidget.Widget); ChildNode->bAffectedByMarquee = SelectionToVisualize->Contains(ChildNode->GetObjectBeingDisplayed()); LayerId = CurWidget.Widget->Paint(Args.WithNewParent(this), CurWidget.Geometry, MyClippingRect, OutDrawElements, NodesLayerId, InWidgetStyle, ShouldBeEnabled(bParentEnabled)); ChildNode->bAffectedByMarquee = false; } // Draw editable world bounds if (!WorldModel->IsSimulating()) { float ScreenSpaceSize = FLevelCollectionModel::EditableAxisLength()*GetZoomAmount()*2.f; FVector2D PaintSize = FVector2D(ScreenSpaceSize, ScreenSpaceSize); FVector2D PaintPosition = GraphCoordToPanelCoord(FVector2D::ZeroVector) - (PaintSize*0.5f); float Scale = 0.2f; // Scale down drawing border FSlateLayoutTransform LayoutTransform(Scale, AllottedGeometry.GetAccumulatedLayoutTransform().GetTranslation() + PaintPosition); FSlateRenderTransform RenderTransform(Scale, AllottedGeometry.GetAccumulatedRenderTransform().GetTranslation() + PaintPosition); FPaintGeometry EditableArea(LayoutTransform, RenderTransform, PaintSize/Scale); FLinearColor PaintColor = FLinearColor::Yellow; PaintColor.A = 0.4f; FSlateDrawElement::MakeBox( OutDrawElements, ++LayerId, EditableArea, FEditorStyle::GetBrush(TEXT("Graph.CompactNode.ShadowSelected")), MyClippingRect, ESlateDrawEffect::None, PaintColor ); } // Draw the marquee selection rectangle PaintMarquee(AllottedGeometry, MyClippingRect, OutDrawElements, ++LayerId); // Draw the software cursor PaintSoftwareCursor(AllottedGeometry, MyClippingRect, OutDrawElements, ++LayerId); if(WorldModel->IsSimulating()) { // Draw a surrounding indicator when PIE is active, to make it clear that the graph is read-only, etc... FSlateDrawElement::MakeBox( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), FEditorStyle::GetBrush(TEXT("Graph.PlayInEditor")), MyClippingRect ); } // Draw observer location { FVector ObserverPosition; FRotator ObserverRotation; if (WorldModel->GetObserverView(ObserverPosition, ObserverRotation)) { FVector2D ObserverPositionScreen = GraphCoordToPanelCoord(FVector2D(ObserverPosition.X, ObserverPosition.Y)); const FSlateBrush* CameraImage = FEditorStyle::GetBrush(TEXT("WorldBrowser.SimulationViewPositon")); FPaintGeometry PaintGeometry = AllottedGeometry.ToPaintGeometry( ObserverPositionScreen - CameraImage->ImageSize*0.5f, CameraImage->ImageSize ); FSlateDrawElement::MakeRotatedBox( OutDrawElements, ++LayerId, PaintGeometry, CameraImage, MyClippingRect, ESlateDrawEffect::None, FMath::DegreesToRadians(ObserverRotation.Yaw), CameraImage->ImageSize*0.5f, FSlateDrawElement::RelativeToElement ); } FVector PlayerPosition; FRotator PlayerRotation; if (WorldModel->GetPlayerView(PlayerPosition, PlayerRotation)) { FVector2D PlayerPositionScreen = GraphCoordToPanelCoord(FVector2D(PlayerPosition.X, PlayerPosition.Y)); const FSlateBrush* CameraImage = FEditorStyle::GetBrush(TEXT("WorldBrowser.SimulationViewPositon")); FPaintGeometry PaintGeometry = AllottedGeometry.ToPaintGeometry( PlayerPositionScreen - CameraImage->ImageSize*0.5f, CameraImage->ImageSize ); FSlateDrawElement::MakeRotatedBox( OutDrawElements, ++LayerId, PaintGeometry, CameraImage, MyClippingRect, ESlateDrawEffect::None, FMath::DegreesToRadians(PlayerRotation.Yaw), CameraImage->ImageSize*0.5f, FSlateDrawElement::RelativeToElement, FLinearColor(FColorList::Orange) ); } } LayerId = PaintScaleRuler(AllottedGeometry, MyClippingRect, OutDrawElements, LayerId); return LayerId; }