// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Misc/Attribute.h" #include "GameFramework/Actor.h" #include "Input/Reply.h" #include "Widgets/SWidget.h" #include "Widgets/SNullWidget.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SCompoundWidget.h" #include "BlueprintUtilities.h" #include "Framework/Commands/InputChord.h" #include "Framework/Commands/UICommandList.h" #include "Engine/LevelStreaming.h" class UEdGraph; struct FNotificationInfo; struct Rect; class FMenuBuilder; class FAssetEditorToolkit; DECLARE_DELEGATE_ThreeParams(FOnNodeTextCommitted, const FText&, ETextCommit::Type, UEdGraphNode*); DECLARE_DELEGATE_RetVal_ThreeParams(bool, FOnNodeVerifyTextCommit, const FText&, UEdGraphNode*, FText&); DECLARE_MULTICAST_DELEGATE(FOnGraphContentMenuDismissed); typedef TSet FGraphPanelSelectionSet; /** Info about how to draw the graph */ struct FGraphAppearanceInfo { FGraphAppearanceInfo() : CornerImage(NULL), InstructionFade(1.f) { } /** Image to draw in corner of graph */ const FSlateBrush* CornerImage; /** Text to write in corner of graph */ FText CornerText; /** If set, will be used as override for PIE notify text */ FText PIENotifyText; /** If set, will be used as override for read only text */ FText ReadOnlyText; /** Text to display if the graph is empty (to guide the user on what to do) */ FText InstructionText; /** Allows graphs to nicely fade instruction text (or completely hide it). */ TAttribute InstructionFade; }; /** Struct used to return info about action menu */ struct FActionMenuContent { explicit FActionMenuContent(TSharedRef InContent, TSharedPtr InWidgetToFocus = TSharedPtr()) : Content(InContent), WidgetToFocus(InWidgetToFocus) { } FActionMenuContent() : Content(SNullWidget::NullWidget) { } TSharedRef Content; TSharedPtr WidgetToFocus; FOnGraphContentMenuDismissed OnMenuDismissed; }; /** * Interface and wrapper for GraphEditor widgets. * Gracefully handles the GraphEditorModule being unloaded. */ class SGraphEditor: public SCompoundWidget { public: DECLARE_DELEGATE_OneParam(FOnSelectionChanged, const FGraphPanelSelectionSet&) DECLARE_DELEGATE_OneParam(FOnFocused, const TSharedRef&); DECLARE_DELEGATE_ThreeParams(FOnDropActor, const TArray>&, class UEdGraph*, const FVector2D&); DECLARE_DELEGATE_ThreeParams(FOnDropStreamingLevel, const TArray>&, class UEdGraph*, const FVector2D&); DECLARE_DELEGATE(FActionMenuClosed); DECLARE_DELEGATE_RetVal_FiveParams(FActionMenuContent, FOnCreateActionMenu, UEdGraph*, const FVector2D&, const TArray&, bool, FActionMenuClosed); DECLARE_DELEGATE_RetVal_FiveParams(FActionMenuContent, FOnCreateNodeOrPinMenu, UEdGraph*, const UEdGraphNode*, const UEdGraphPin*, FMenuBuilder*, bool); DECLARE_DELEGATE_RetVal_TwoParams(FReply, FOnSpawnNodeByShortcut, FInputChord, const FVector2D&); DECLARE_DELEGATE(FOnNodeSpawnedByKeymap); DECLARE_DELEGATE_TwoParams(FOnDisallowedPinConnection, const UEdGraphPin*, const UEdGraphPin*); /** Info about events occurring in/on the graph */ struct FGraphEditorEvents { /** Called when selection changes */ FOnSelectionChanged OnSelectionChanged; /** Called when a node is double clicked */ FSingleNodeEvent OnNodeDoubleClicked; /* Called when focus moves to graph */ FOnFocused OnFocused; /* Called when an actor is dropped on graph */ FOnDropActor OnDropActor; /* Called when a streaming level is dropped on graph */ FOnDropStreamingLevel OnDropStreamingLevel; /** Called when text is being committed on the graph to verify */ FOnNodeVerifyTextCommit OnVerifyTextCommit; /** Called when text is committed on the graph */ FOnNodeTextCommitted OnTextCommitted; /** Called to create context menu for right clicking in empty area */ FOnCreateActionMenu OnCreateActionMenu; /** Called to create context menu for right clicking a node or pin, same parameters as GetContextMenuActions on schema */ FOnCreateNodeOrPinMenu OnCreateNodeOrPinMenu; /** Called to spawn a node in the graph using a shortcut */ FOnSpawnNodeByShortcut OnSpawnNodeByShortcut; /** Called when a keymap spawns a node */ FOnNodeSpawnedByKeymap OnNodeSpawnedByKeymap; /** Called when the user generates a warning tooltip because a connection was invalid */ FOnDisallowedPinConnection OnDisallowedPinConnection; }; SLATE_BEGIN_ARGS(SGraphEditor) : _AdditionalCommands(static_cast(NULL)), _IsEditable(true), _DisplayAsReadOnly(false), _IsEmpty(false), _GraphToEdit(NULL), _GraphToDiff(NULL), _AutoExpandActionMenu(false), _ShowGraphStateOverlay(true) {} SLATE_ARGUMENT(TSharedPtr, AdditionalCommands) SLATE_ATTRIBUTE(bool, IsEditable) SLATE_ATTRIBUTE(bool, DisplayAsReadOnly) SLATE_ATTRIBUTE(bool, IsEmpty) SLATE_ARGUMENT(TSharedPtr, TitleBar) SLATE_ATTRIBUTE(FGraphAppearanceInfo, Appearance) SLATE_EVENT(FEdGraphEvent, OnGraphModuleReloaded) SLATE_ARGUMENT(UEdGraph*, GraphToEdit) SLATE_ARGUMENT(UEdGraph*, GraphToDiff) SLATE_ARGUMENT(FGraphEditorEvents, GraphEvents) SLATE_ARGUMENT(bool, AutoExpandActionMenu) SLATE_ARGUMENT(TWeakPtr, AssetEditorToolkit) SLATE_EVENT(FSimpleDelegate, OnNavigateHistoryBack) SLATE_EVENT(FSimpleDelegate, OnNavigateHistoryForward) /** Show overlay elements for the graph state such as the PIE and read-only borders and text */ SLATE_ATTRIBUTE(bool, ShowGraphStateOverlay) SLATE_END_ARGS() /** * Loads the GraphEditorModule and constructs a GraphEditor as a child of this widget. * * @param InArgs Declaration params from which to construct the widget. */ UNREALED_API void Construct(const FArguments& InArgs); /** @return The current graph being edited */ UEdGraph* GetCurrentGraph() const { return EdGraphObj; } virtual FVector2D GetPasteLocation() const { if (Implementation.IsValid()) { return Implementation->GetPasteLocation(); } else { return FVector2D::ZeroVector; } } /* Set new viewer location and optionally set the current bookmark */ virtual void SetViewLocation(const FVector2D& Location, float ZoomAmount, const FGuid& BookmarkId = FGuid()) { if (Implementation.IsValid()) { Implementation->SetViewLocation(Location, ZoomAmount, BookmarkId); } } /** * Gets the view location of the graph * * @param OutLocation Will have the current view location * @param OutZoomAmount Will have the current zoom amount */ virtual void GetViewLocation(FVector2D& OutLocation, float& OutZoomAmount) { if (Implementation.IsValid()) { Implementation->GetViewLocation(OutLocation, OutZoomAmount); } } /** * Gets the current graph view bookmark * * @param OutBookmarkId Will have the current bookmark ID */ virtual void GetViewBookmark(FGuid& OutBookmarkId) { if (Implementation.IsValid()) { Implementation->GetViewBookmark(OutBookmarkId); } } /** Check if node title is visible with optional flag to ensure it is */ virtual bool IsNodeTitleVisible(const class UEdGraphNode* Node, bool bRequestRename) { bool bResult = false; if (Implementation.IsValid()) { bResult = Implementation->IsNodeTitleVisible(Node, bRequestRename); } return bResult; } /* Lock two graph editors together */ virtual void LockToGraphEditor(TWeakPtr Other) { if (Implementation.IsValid()) { Implementation->LockToGraphEditor(Other); } } /* Unlock two graph editors from each other */ virtual void UnlockFromGraphEditor(TWeakPtr Other) { if (Implementation.IsValid()) { Implementation->UnlockFromGraphEditor(Other); } } /** Bring the specified node into view */ virtual void JumpToNode(const class UEdGraphNode* JumpToMe, bool bRequestRename = false, bool bSelectNode = true) { if (Implementation.IsValid()) { Implementation->JumpToNode(JumpToMe, bRequestRename, bSelectNode); } } /** Bring the specified pin into view */ virtual void JumpToPin(const class UEdGraphPin* JumpToMe) { if (Implementation.IsValid()) { Implementation->JumpToPin(JumpToMe); } } /** Pin visibility modes */ enum EPinVisibility { Pin_Show, Pin_HideNoConnection, Pin_HideNoConnectionNoDefault }; /*Set the pin visibility mode*/ virtual void SetPinVisibility(EPinVisibility InVisibility) { if (Implementation.IsValid()) { Implementation->SetPinVisibility(InVisibility); } } /** Register an active timer on the graph editor. */ virtual TSharedRef RegisterActiveTimer(float TickPeriod, FWidgetActiveTimerDelegate TickFunction) { if (Implementation.IsValid()) { return Implementation->RegisterActiveTimer(TickPeriod, TickFunction); } return TSharedPtr().ToSharedRef(); } /** @return a reference to the list of selected graph nodes */ virtual const FGraphPanelSelectionSet& GetSelectedNodes() const { static FGraphPanelSelectionSet NoSelection; if (Implementation.IsValid()) { return Implementation->GetSelectedNodes(); } else { return NoSelection; } } /** Clear the selection */ virtual void ClearSelectionSet() { if (Implementation.IsValid()) { Implementation->ClearSelectionSet(); } } /** Set the selection status of a node */ virtual void SetNodeSelection(UEdGraphNode* Node, bool bSelect) { if (Implementation.IsValid()) { Implementation->SetNodeSelection(Node, bSelect); } } /** Select all nodes */ virtual void SelectAllNodes() { if (Implementation.IsValid()) { Implementation->SelectAllNodes(); } } virtual class UEdGraphPin* GetGraphPinForMenu() { if (Implementation.IsValid()) { return Implementation->GetGraphPinForMenu(); } else { return NULL; } } virtual class UEdGraphNode* GetGraphNodeForMenu() { if (Implementation.IsValid()) { return Implementation->GetGraphNodeForMenu(); } else { return NULL; } } // Zooms out to fit either all nodes or only the selected ones virtual void ZoomToFit(bool bOnlySelection) { if (Implementation.IsValid()) { return Implementation->ZoomToFit(bOnlySelection); } } /** Get Bounds for selected nodes, false if nothing selected*/ virtual bool GetBoundsForSelectedNodes(class FSlateRect& Rect, float Padding) { if (Implementation.IsValid()) { return Implementation->GetBoundsForSelectedNodes(Rect, Padding); } return false; } /** Get Bounds for the specified node, returns false on failure */ virtual bool GetBoundsForNode(const UEdGraphNode* InNode, class FSlateRect& Rect, float Padding) const { if (Implementation.IsValid()) { return Implementation->GetBoundsForNode(InNode, Rect, Padding); } return false; } virtual void StraightenConnections() { if (Implementation.IsValid()) { return Implementation->StraightenConnections(); } } virtual void StraightenConnections(UEdGraphPin* SourcePin, UEdGraphPin* PinToAlign = nullptr) const { if (Implementation.IsValid()) { return Implementation->StraightenConnections(SourcePin, PinToAlign); } } virtual void RefreshNode(UEdGraphNode& Node) { if (Implementation.IsValid()) { return Implementation->RefreshNode(Node); } } // Invoked to let this widget know that the GraphEditor module has been reloaded UNREALED_API void OnModuleReloaded(); // Invoked to let this widget know that the GraphEditor module is being unloaded. UNREALED_API void OnModuleUnloading(); UNREALED_API void NotifyPrePropertyChange(const FString& PropertyName); UNREALED_API void NotifyPostPropertyChange(const FPropertyChangedEvent& PropertyChangeEvent, const FString& PropertyName); /** Invoked when the Graph being edited changes in some way. */ virtual void NotifyGraphChanged() { if (Implementation.IsValid()) { Implementation->NotifyGraphChanged(); } } /* Get the title bar if there is one */ virtual TSharedPtr GetTitleBar() const { if (Implementation.IsValid()) { return Implementation->GetTitleBar(); } return TSharedPtr(); } /** Show notification on graph */ virtual void AddNotification(FNotificationInfo& Info, bool bSuccess) { if (Implementation.IsValid()) { Implementation->AddNotification(Info, bSuccess); } } /** Capture keyboard */ virtual void CaptureKeyboard() { if (Implementation.IsValid()) { Implementation->CaptureKeyboard(); } } /** Sets the current node, pin and connection factory. */ virtual void SetNodeFactory(const TSharedRef& NewNodeFactory) { if (Implementation.IsValid()) { Implementation->SetNodeFactory(NewNodeFactory); } } /** Common methods for MaterialEditor and BlueprintEditor's focusing related nodes feature */ UNREALED_API void ResetAllNodesUnrelatedStates(); UNREALED_API void FocusCommentNodes(TArray& CommentNodes, TArray& RelatedNodes); virtual void OnAlignTop() { if (Implementation.IsValid()) { Implementation->OnAlignTop(); } } virtual void OnAlignMiddle() { if (Implementation.IsValid()) { Implementation->OnAlignMiddle(); } } virtual void OnAlignBottom() { if (Implementation.IsValid()) { Implementation->OnAlignBottom(); } } virtual void OnAlignLeft() { if (Implementation.IsValid()) { Implementation->OnAlignLeft(); } } virtual void OnAlignCenter() { if (Implementation.IsValid()) { Implementation->OnAlignCenter(); } } virtual void OnAlignRight() { if (Implementation.IsValid()) { Implementation->OnAlignRight(); } } virtual void OnStraightenConnections() { if (Implementation.IsValid()) { Implementation->OnStraightenConnections(); } } virtual void OnDistributeNodesH() { if (Implementation.IsValid()) { Implementation->OnDistributeNodesH(); } } virtual void OnDistributeNodesV() { if (Implementation.IsValid()) { Implementation->OnDistributeNodesV(); } } virtual int32 GetNumberOfSelectedNodes() const { if (Implementation.IsValid()) { return Implementation->GetNumberOfSelectedNodes(); } return 0; } /** Returns the currently selected node if there is a single node selected (if there are multiple nodes selected or none selected, it will return nullptr) */ virtual UEdGraphNode* GetSingleSelectedNode() const { if (Implementation.IsValid()) { return Implementation->GetSingleSelectedNode(); } return nullptr; } // Returns the first graph editor that is viewing the specified graph UNREALED_API static TSharedPtr FindGraphEditorForGraph(const UEdGraph* Graph); protected: /** Invoked when the underlying Graph is being changed. */ virtual void OnGraphChanged(const struct FEdGraphEditAction& InAction) { if (Implementation.IsValid()) { Implementation->OnGraphChanged(InAction); } } private: static void RegisterGraphEditor(const TSharedRef& InGraphEditor); void ConstructImplementation(const FArguments& InArgs); protected: /** The Graph we are currently editing */ UEdGraph* EdGraphObj; TWeakPtr AssetEditorToolkit; private: /** The actual implementation of the GraphEditor */ TSharedPtr Implementation; /** Active GraphEditor wrappers; we will notify these about the module being unloaded so they can handle it gracefully. */ UNREALED_API static TArray> AllInstances; // This callback is triggered whenever the graph module is reloaded FEdGraphEvent OnGraphModuleReloadedCallback; // The graph editor module needs to access AllInstances, but no-one else should be able to friend class FGraphEditorModule; };