// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Animation/CurveSequence.h" #include "Containers/Array.h" #include "Containers/Map.h" #include "Containers/Set.h" #include "Containers/UnrealString.h" #include "CoreMinimal.h" #include "DiffResults.h" #include "Framework/Commands/InputChord.h" #include "GraphEditor.h" #include "HAL/PlatformCrt.h" #include "Input/CursorReply.h" #include "Input/Reply.h" #include "Internationalization/Text.h" #include "Layout/ArrangedChildren.h" #include "Layout/ArrangedWidget.h" #include "Layout/BasicLayoutWidgetSlot.h" #include "Layout/Children.h" #include "Layout/ChildrenBase.h" #include "Layout/Geometry.h" #include "Layout/LayoutUtils.h" #include "Layout/Margin.h" #include "Layout/SlateRect.h" #include "Layout/Visibility.h" #include "MarqueeOperation.h" #include "Math/Color.h" #include "Math/UnrealMathSSE.h" #include "Math/Vector2D.h" #include "Misc/Attribute.h" #include "Misc/Guid.h" #include "SlotBase.h" #include "Styling/AppStyle.h" #include "Styling/CoreStyle.h" #include "Styling/ISlateStyle.h" #include "Styling/SlateColor.h" #include "Templates/SharedPointer.h" #include "Templates/UniquePtr.h" #include "Templates/UnrealTemplate.h" #include "Types/PaintArgs.h" #include "Types/SlateEnums.h" #include "Types/SlateVector2.h" #include "Types/WidgetMouseEventsDelegate.h" #include "UObject/GCObject.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SPanel.h" #include "Widgets/SWidget.h" #include "SNodePanel.generated.h" class FActiveTimerHandle; class FReferenceCollector; class FScopedTransaction; class FSlateWindowElementList; class FWidgetStyle; class UObject; struct FCaptureLostEvent; struct FDiffSingleResult; struct FFocusEvent; struct FKeyEvent; struct FMarqueeOperation; struct FPointerEvent; struct FSlateBrush; struct Rect; //@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, }; } UENUM() enum class EGraphZoomLimitHandling : uint8 { /** By default, hold down Ctrl to break past the zoom limit. */ Default, /** Always allow breaking through the zoom limit. */ AllowLimitBreak, /** Never allow breaking through the zoom limit. */ DisallowLimitBreak }; // 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 */ FDeprecateSlateVector2D AnimationEnvelope; /** Offset origin of the overlay from the widget */ FDeprecateSlateVector2D 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) { } }; // Entry for an overlay widget in the node panel struct FOverlayWidgetInfo { public: /** Widget to use */ TSharedPtr Widget; /** Offset origin of the overlay from the widget */ FDeprecateSlateVector2D OverlayOffset; public: FOverlayWidgetInfo() : Widget(nullptr) , OverlayOffset(0.f, 0.f) { } FOverlayWidgetInfo(TSharedPtr InWidget) : Widget(InWidget) , OverlayOffset(0.f, 0.f) { } }; // 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; virtual EGraphZoomLimitHandling GetZoomLimitHandling() const { return EGraphZoomLimitHandling::Default; } // Necessary for Mac OS X to compile 'delete ;' virtual ~FZoomLevelsContainer( void ) {}; }; struct GRAPHEDITOR_API FGraphSelectionManager : public FGCObject { TSet> 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); void AddReferencedObjects(FReferenceCollector& Collector) override; virtual FString GetReferencerName() const override { return TEXT("FGraphSelectionManager"); } }; /** * 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. */ namespace ENodeZone { enum Type { TopLeft, TopCenter, TopRight, Left, Center, Right, BottomLeft, BottomCenter, BottomRight, Count }; } class GRAPHEDITOR_API SNodePanel : public SPanel { public: class SNode : public SPanel { public: /** A slot that support alignment of content and padding and z-order */ class GRAPHEDITOR_API FNodeSlot : public TSlotBase, public TAlignmentWidgetSlotMixin { public: friend SNode; FNodeSlot() : FNodeSlot(ENodeZone::TopLeft) { } FNodeSlot(ENodeZone::Type InZone) : TSlotBase() , TAlignmentWidgetSlotMixin(HAlign_Fill, VAlign_Fill) , Zone(InZone) , SlotPadding(0.0f) , Offset(FVector2f::ZeroVector) , AllowScale(true) { } SLATE_SLOT_BEGIN_ARGS_OneMixin(FNodeSlot, TSlotBase, TAlignmentWidgetSlotMixin) SLATE_ATTRIBUTE(FMargin, Padding) SLATE_ATTRIBUTE_DEPRECATED(FVector2D, SlotOffset, 5.6, "Use SlotOffset2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") SLATE_ATTRIBUTE(FVector2f, SlotOffset2f) SLATE_ATTRIBUTE_DEPRECATED(FVector2D, SlotSize, 5.6, "UseSlotSize2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") SLATE_ATTRIBUTE(FVector2f, SlotSize2f) SLATE_ATTRIBUTE(bool, AllowScaling) SLATE_SLOT_END_ARGS() void Construct(const FChildren& SlotOwner, FSlotArguments&& InArgs); public: ENodeZone::Type GetZoneType() const { return Zone; } void SetPadding(TAttribute InPadding) { SlotPadding = MoveTemp(InPadding); } FMargin GetPadding() const { return SlotPadding.Get(); } UE_DEPRECATED(5.6, "This exists as a temporary bridge for deprecation of SlotSize Attribute. Please use the FVector2f version.") void SetSlotOffset(TAttribute InOffset) { PRAGMA_DISABLE_DEPRECATION_WARNINGS Offset_Deprecated = MoveTemp(InOffset); Offset.BindLambda([this]() { return UE::Slate::CastToVector2f(Offset_Deprecated.Get()); }); PRAGMA_ENABLE_DEPRECATION_WARNINGS } void SetSlotOffset(TAttribute InOffset) { Offset = MoveTemp(InOffset); } UE::Slate::FDeprecateVector2DResult GetSlotOffset() const { return Offset.Get(); } UE_DEPRECATED(5.6, "This exists as a temporary bridge for deprecation of SlotSize Attribute. Please use the FVector2f version.") void SetSlotSize(TAttribute InSize) { PRAGMA_DISABLE_DEPRECATION_WARNINGS Size_Deprecated = MoveTemp(InSize); Offset.BindLambda([this]() { return UE::Slate::CastToVector2f(Size_Deprecated.Get()); }); PRAGMA_ENABLE_DEPRECATION_WARNINGS } void SetSlotSize(TAttribute InSize) { Size = MoveTemp(InSize); } UE::Slate::FDeprecateVector2DResult GetSlotSize() const { return Size.Get(); } void SetAllowScaling(TAttribute InAllowScaling) { AllowScale = MoveTemp(InAllowScaling); } bool GetAllowScaling() const { return AllowScale.Get(); } private: /** The child widget contained in this slot. */ ENodeZone::Type Zone; TAttribute SlotPadding; TAttribute Offset; TAttribute Size; UE_DEPRECATED(5.6, "This exists as a temporary bridge for deprecation of SlotSize Attribute.") TAttribute Size_Deprecated; UE_DEPRECATED(5.6, "This exists as a temporary bridge for deprecation of SlotSize Attribute.") TAttribute Offset_Deprecated; TAttribute AllowScale; }; typedef TSet> FNodeSet; //~ Begin SWidget Interface virtual void OnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override { SPanel::OnMouseEnter(InGeometry, InMouseEvent); if (TSharedPtr ParentPanel = GetParentPanel()) { ParentPanel->SetCurrentHoveredNode(GetObjectBeingDisplayed()); } } virtual void OnMouseLeave(const FPointerEvent& InMouseEvent) override { SPanel::OnMouseLeave(InMouseEvent); if (TSharedPtr ParentPanel = GetParentPanel()) { UObject* PreviousHoveredNode = ParentPanel->GetCurrentHoveredNode(); // Only unset the current hovered node on mouse leave if its this node if (PreviousHoveredNode == GetObjectBeingDisplayed()) { ParentPanel->SetCurrentHoveredNode(nullptr); } } } //~ End SWidget Interface // SPanel Interface virtual FChildren* GetChildren() override { return &Children; } virtual FVector2D ComputeDesiredSize(float) const override { 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 ( FVector2D(CenterZone.GetWidget()->GetDesiredSize() + CenterZone.SlotPadding.Get().GetDesiredSize()) ) * FVector2D(DesiredSizeScale.Get()); } } } return FVector2D::ZeroVector; } virtual float GetRelativeLayoutScale(int32 ChildIndex, float LayoutScaleMultiplier) const override { const FNodeSlot& ThisSlot = Children[ChildIndex]; if (!ThisSlot.AllowScale.Get()) { // Child slots that do not allow zooming should scale themselves to negate the node panel's zoom. TSharedPtr ParentPanel = GetParentPanel(); if (ParentPanel.IsValid()) { return 1.0f / ParentPanel->GetZoomAmount(); } } return 1.0f; } 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()); // If this child is not allowed to scale, its scale relative to its parent should undo the parent widget's scaling. FVector2f Size; if( CurChild.Size.IsSet() ) { Size = CurChild.Size.Get(); } else { AlignmentArrangeResult XResult = AlignChild(AllottedGeometry.GetLocalSize().X, CurChild, SlotPadding); AlignmentArrangeResult YResult = AlignChild(AllottedGeometry.GetLocalSize().Y, CurChild, SlotPadding); Size = FVector2f( XResult.Size, YResult.Size ); } const FArrangedWidget ChildGeom = AllottedGeometry.MakeChild( CurChild.GetWidget(), CurChild.Offset.Get(), Size, GetRelativeLayoutScale(ChildIndex, AllottedGeometry.Scale) ); ArrangedChildren.AddWidget( ChildVisibility, ChildGeom ); } } } virtual int32 OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const override { FArrangedChildren ArrangedChildren( EVisibility::Visible ); { ArrangeChildren( AllottedGeometry, ArrangedChildren ); } int32 MaxLayerId = LayerId; for( int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex ) { const FArrangedWidget& CurWidget = ArrangedChildren[ ChildIndex ]; if (!IsChildWidgetCulled(MyCullingRect, CurWidget)) { const int32 CurWidgetsMaxLayerId = CurWidget.Widget->Paint(Args.WithNewParent(this), CurWidget.Geometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, ShouldBeEnabled(bParentEnabled)); MaxLayerId = FMath::Max(MaxLayerId, CurWidgetsMaxLayerId); } else { //SlateGI - RemoveContent } } return MaxLayerId; } // End of SPanel Interface using FScopedWidgetSlotArguments = TPanelChildren::FScopedWidgetSlotArguments; FScopedWidgetSlotArguments GetOrAddSlot( const ENodeZone::Type SlotId ) { // Return existing int32 InsertIndex = INDEX_NONE; for( int32 ChildIndex = 0; ChildIndex < Children.Num(); ++ChildIndex ) { if( Children[ ChildIndex ].Zone == SlotId ) { Children.RemoveAt(ChildIndex); InsertIndex = ChildIndex; } } // Add new return FScopedWidgetSlotArguments{ MakeUnique(SlotId), Children, InsertIndex }; } 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; } } } /** * @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. * @param bMarkDirty If we should mark nodes as dirty on move */ UE_DEPRECATED(5.6, "Use the version of the function accepting FVector2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") virtual void MoveTo(const FVector2D& NewPosition, FNodeSet& NodeFilter, bool bMarkDirty = true) UE_SLATE_DEPRECATED_VECTOR_VIRTUAL_FUNCTION { MoveTo(UE::Slate::CastToVector2f(NewPosition), NodeFilter, bMarkDirty); } virtual void MoveTo(const FVector2f& NewPosition, FNodeSet& NodeFilter, bool bMarkDirty = true) { } /** @return the Node's position within the graph */ UE_DEPRECATED(5.6, "Use the version of the function accepting FVector2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") virtual FVector2D GetPosition() const UE_SLATE_DEPRECATED_VECTOR_VIRTUAL_FUNCTION { return FVector2D(GetPosition2f()); } virtual FVector2f GetPosition2f() const { return FVector2f::ZeroVector; } /** @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 { return bSelected ? FAppStyle::GetBrush(TEXT("Graph.Node.ShadowSelected")) : FAppStyle::GetBrush(TEXT("Graph.Node.Shadow")); } struct DiffHighlightInfo { const FSlateBrush* Brush; const FLinearColor Tint; }; /** @return Collection of brushes layered to outline node with the DiffResult color */ TArray 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")); } /** Populate the brushes array with any overlay brushes to render */ UE_DEPRECATED(5.6, "Use the version of the function accepting FVector2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") virtual void GetOverlayBrushes(bool bSelected, const FVector2D WidgetSize, TArray& Brushes) const UE_SLATE_DEPRECATED_VECTOR_VIRTUAL_FUNCTION { GetOverlayBrushes(bSelected, UE::Slate::CastToVector2f(WidgetSize), Brushes); } virtual void GetOverlayBrushes(bool bSelected, const FVector2f& WidgetSize, TArray& Brushes) const { } /** Populate the widgets array with any overlay widgets to render */ UE_DEPRECATED(5.6, "Use the version of the function accepting FVector2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") virtual TArray GetOverlayWidgets(bool bSelected, const FVector2D& WidgetSize) const UE_SLATE_DEPRECATED_VECTOR_VIRTUAL_FUNCTION { return GetOverlayWidgets(bSelected, UE::Slate::CastToVector2f(WidgetSize)); } virtual TArray GetOverlayWidgets(bool bSelected, const FVector2f& WidgetSize) const { return TArray(); } /** Populate the popups array with any popups to render */ virtual void GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const { } /** 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 >& 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 */ UE_DEPRECATED(5.6, "Use the version of the function accepting FVector2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") virtual bool CanBeSelected(const FVector2D& MousePositionInNode) const UE_SLATE_DEPRECATED_VECTOR_VIRTUAL_FUNCTION { return CanBeSelected(UE::Slate::CastToVector2f(MousePositionInNode)); } virtual bool CanBeSelected(const FVector2f& MousePositionInNode) const { return true; } /** Called when user interaction has completed */ virtual void EndUserInteraction() const {} /** * 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 */ UE_DEPRECATED(5.6, "Use the version of the function accepting FVector2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") virtual FVector2D GetDesiredSizeForMarquee() const UE_SLATE_DEPRECATED_VECTOR_VIRTUAL_FUNCTION { return FVector2D(GetDesiredSizeForMarquee2f()); } virtual FVector2f GetDesiredSizeForMarquee2f() const { return UE::Slate::CastToVector2f(GetDesiredSize()); } // 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(); } void SetParentPanel(const TSharedPtr& InParent) { ParentPanelPtr = InParent; } protected: SNode() : BorderImage( FCoreStyle::Get().GetBrush( "NoBorder" ) ) , BorderBackgroundColor( FAppStyle::GetColor("Graph.ForegroundColor")) , DesiredSizeScale(FVector2f::UnitVector) , Children(this) { bHasRelativeLayoutScale = true; } protected: // SBorder Begin TAttribute BorderImage; TAttribute BorderBackgroundColor; TAttribute DesiredSizeScale; /** Whether or not to show the disabled effect when this border is disabled */ TAttribute 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 ContentScale; /** The color and opacity to apply to this widget and all its descendants. */ TAttribute ColorAndOpacity; /** Optional foreground color that will be inherited by all of this widget's contents */ TAttribute ForegroundColor; // SPanel End private: TSharedPtr GetParentPanel() const { return ParentPanelPtr.Pin(); } TPanelChildren Children; TWeakPtr ParentPanelPtr; }; SNodePanel(); // SPanel interface virtual void OnArrangeChildren(const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren) const override; virtual FVector2D ComputeDesiredSize(float) const override; virtual FChildren* GetChildren() override; // End of SPanel interface // SWidget interface virtual void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) override; virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual void OnMouseCaptureLost(const FCaptureLostEvent& CaptureLostEvent) override; 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; 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; virtual FReply OnTouchGesture( const FGeometry& MyGeometry, const FPointerEvent& GestureEvent ) override; virtual FReply OnTouchEnded( const FGeometry& MyGeometry, const FPointerEvent& InTouchEvent ) override; virtual float GetRelativeLayoutScale(int32 ChildIndex, float LayoutScaleMultiplier) const override; // 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 */ UE::Slate::FDeprecateVector2DResult GetViewOffset() const; /** * 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 */ UE_DEPRECATED(5.6, "Use the version of the function accepting FVector2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") bool GetZoomTargetRect(FVector2D& TopLeft, FVector2D& BottomRight) const; bool GetZoomTargetRect(FVector2f& TopLeft, FVector2f& BottomRight) const; /** @return the current view bookmark ID */ const FGuid& GetViewBookmarkId() const { return CurrentBookmarkGuid; } /** 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 */ UE::Slate::FDeprecateVector2DResult PanelCoordToGraphCoord(const UE::Slate::FDeprecateVector2DParameter& PanelSpaceCoordinate) const; /** Restore the graph panel to the supplied view offset/zoom. Also, optionally set the current bookmark ID. */ void RestoreViewSettings(const UE::Slate::FDeprecateVector2DParameter& InViewOffset, float InZoomAmount, const FGuid& InBookmarkGuid = FGuid()); /** Get the grid snap size */ static uint32 GetSnapGridSize(); /** * 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) */ 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) */ UE::Slate::FDeprecateVector2DResult GetPastePosition() const; /** Ask panel to scroll to location */ void RequestDeferredPan(const UE::Slate::FDeprecateVector2DParameter& TargetPosition); /** If it is focusing on a particular object */ bool HasDeferredObjectFocus() const; /** Query whether this graph is about to start panning/zooming towards a destination */ bool HasDeferredZoomDestination() const; /** Commit transactions for any node movements */ void FinalizeNodeMovements(); /** 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; /** Returns all the panel children rather than only visible */ FChildren* GetAllChildren(); protected: /** Initialize members */ void Construct(); /** * 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 FVector2f& TopLeft, const FVector2f& BottomRight); /** Update the new view offset location */ void UpdateViewOffset(const FGeometry& MyGeometry, const FVector2f& TargetPosition); /** Compute much panel needs to change to pan to location */ static FVector2f ComputeEdgePanAmount(const FGeometry& MyGeometry, const FVector2f& 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 */ FVector2f GraphCoordToPanelCoord(const UE::Slate::FDeprecateVector2DParameter& 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& InNodeToDrag, const FVector2D& GrabOffset) UE_SLATE_DEPRECATED_VECTOR_VIRTUAL_FUNCTION; virtual void OnBeginNodeInteraction(const TSharedRef& InNodeToDrag, const FVector2f& 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& 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, TSet>& 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); /** * 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); /** Add a slot to the CanvasPanel dynamically */ virtual void AddGraphNode(const TSharedRef& NodeToAdd); /** Remove all nodes from the panel */ virtual void RemoveAllNodes(); /** Populate visibile children array */ virtual void PopulateVisibleChildren(const FGeometry& AllottedGeometry); /** Arrange child nodes - allows derived classes to supply non-node children in OnArrangeChildren */ virtual void ArrangeChildNodes(const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren) const; // Paint the background as lines void PaintBackgroundAsLines(const FSlateBrush* BackgroundImage, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32& DrawLayerId) const; // Paint the well shadow (around the perimeter) void PaintSurroundSunkenShadow(const FSlateBrush* ShadowImage, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 DrawLayerId) const; // Paint the marquee selection rectangle void PaintMarquee(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 DrawLayerId) const; // Paint the software mouse if necessary void PaintSoftwareCursor(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 DrawLayerId) const; // Paint a comment bubble void PaintComment(const FString& CommentText, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 DrawLayerId, const FLinearColor& CommentTinting, float& HeightAboveNode, const FWidgetStyle& InWidgetStyle ) const; /** Determines if a specified node is not visually relevant. */ bool IsNodeCulled(const TSharedRef& Node, const FGeometry& AllottedGeometry) const; protected: /////////// // INTERFACE TO IMPLEMENT /////////// /** @return the widget in the summoned context menu that should be focused. */ virtual TSharedPtr OnSummonContextMenu(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { return TSharedPtr(); } virtual bool OnHandleLeftMouseRelease(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { return false; } protected: /** * Get the bounds of the given node * @return True if successful */ UE_DEPRECATED(5.6, "Use the version of the function accepting FVector2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") bool GetBoundsForNode(const UObject* InNode, /*out*/ FVector2D& MinCorner, /*out*/ FVector2D& MaxCorner, float Padding = 0.0f) const; bool GetBoundsForNode(const UObject* InNode, /*out*/ FVector2f& MinCorner, /*out*/ FVector2f& MaxCorner, float Padding = 0.0f) const; /** * 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 */ UE_DEPRECATED(5.6, "Use the version of the function accepting FVector2f; this Slate API no longer interfaces directly with double-precision scalars and vectors.") bool GetBoundsForNodes(bool bSelectionSetOnly, /*out*/ FVector2D& MinCorner, /*out*/ FVector2D& MaxCorner, float Padding = 0.0f) const; bool GetBoundsForNodes(bool bSelectionSetOnly, /*out*/ FVector2f& MinCorner, /*out*/ FVector2f& MaxCorner, float Padding = 0.0f) const; /** * Scroll the view to the desired location * @return true when the desired location is reached */ bool ScrollToLocation(const FGeometry& MyGeometry, FVector2f DesiredCenterPosition, const float InDeltaTime); /** * Zoom to fit the desired size * @return true when zoom fade has completed & fits the desired size */ bool ZoomToLocation(const FVector2f& CurrentSizeWithoutZoom, const FVector2f& DesiredSize, bool bDoneScrolling); /** * Change zoom level by the specified zoom level delta, about the specified origin. */ void ChangeZoomLevel(int32 ZoomLevelDelta, const FVector2f& WidgetSpaceZoomOrigin, bool bOverrideZoomLimiting); // Should be called whenever the zoom level has changed void PostChangedZoom(); // Fires up a per-tick function to zoom the graph to fit void RequestZoomToFit(); // Cancels any active zoom-to-fit action void CancelZoomToFit(); public: // Sets the zoom levels container template void SetZoomLevelsContainer() { ZoomLevels = MakeUnique(); OldZoomAmount = ZoomLevels->GetZoomAmount(ZoomLevel); ZoomLevel = PreviousZoomLevel = ZoomLevels->GetNearestZoomLevel(OldZoomAmount); PostChangedZoom(); } void SetCurrentHoveredNode(UObject* InNode) { CurrentHoveredNode = InNode; } UObject* GetCurrentHoveredNode() { return CurrentHoveredNode; } protected: // The interface for mapping ZoomLevel values to actual node scaling values TUniquePtr ZoomLevels; /** The position within the graph at which the user is looking */ FDeprecateSlateVector2D ViewOffset; /** The position within the graph at which the user was looking last tick */ FDeprecateSlateVector2D 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; /** Are we zooming the view with trackpad at the moment? */ bool bIsZoomingWithTrackpad; /** The graph node widgets owned by this panel */ TSlotlessChildren Children; TSlotlessChildren VisibleChildren; /** The node that the user is dragging. Null when they are not dragging a node. */ TWeakPtr NodeUnderMousePtr; /** Where in the title the user grabbed to initiate the drag */ FDeprecateSlateVector2D NodeGrabOffset; /** The total distance that the mouse has been dragged while down */ float TotalMouseDelta; /** The additive X and Y components of mouse drag (used when zooming) */ float TotalMouseDeltaXY; /** Offset in the panel the user started the LMB+RMB zoom from */ FDeprecateSlateVector2D ZoomStartOffset; /** Cumulative magnify delta from trackpad gesture */ float TotalGestureMagnify; public: /** Nodes selected in this instance of the editor; the selection is per-instance of the GraphEditor */ FGraphSelectionManager SelectionManager; protected: /** The currently hovered SNode's UObject */ TObjectPtr CurrentHoveredNode; /** A pending marquee operation if it's active */ FMarqueeOperation Marquee; /** Is the graph editable (can nodes be moved, etc...)? */ TAttribute IsEditable; /** Given a node, find the corresponding widget */ TMap< UObject*, TSharedRef > NodeToWidgetLookup; /** If not empty and a part of this panel, this node will be selected and brought into view on the next Tick */ TSet DeferredSelectionTargetObjects; /** 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. */ FDeprecateSlateVector2D PastePosition; /** Position to pan to */ FDeprecateSlateVector2D DeferredPanPosition; /** true if pending request for deferred panning */ bool bRequestDeferredPan; /** The current position of the software cursor */ FDeprecateSlateVector2D 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::FOnSpawnNodeByShortcutAtLocation OnSpawnNodeByShortcutAtLocation; /** The last key chord detected in this graph panel */ FInputChord LastKeyChordDetected; /** The current transaction for undo/redo */ TSharedPtr ScopedTransactionPtr; /** Cached geometry for use within the active timer */ FGeometry CachedGeometry; /** 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; /** Node positions pre-drag, used to limit transaction creation on drag */ TMap, FVector2f> OriginalNodePositions; /** Called when the user left clicks on a node without dragging */ SGraphEditor::FOnNodeSingleClicked OnNodeSingleClicked; private: /** Active timer that handles deferred zooming until the target zoom is reached */ EActiveTimerReturnType HandleZoomToFit(double InCurrentTime, float InDeltaTime); private: /** The handle to the active timer */ TWeakPtr ActiveTimerHandle; /** Zoom target rectangle */ FVector2f ZoomTargetTopLeft; FVector2f ZoomTargetBottomRight; /** Current view bookmark info */ FGuid CurrentBookmarkGuid; };