// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/Array.h" #include "Containers/Map.h" #include "Containers/UnrealString.h" #include "Delegates/Delegate.h" #include "Editor.h" #include "Engine/EngineBaseTypes.h" #include "Engine/World.h" #include "HAL/Platform.h" #include "InputCoreTypes.h" #include "InputState.h" #include "InteractiveTool.h" #include "InteractiveToolManager.h" #include "InteractiveToolsContext.h" #include "Math/Ray.h" #include "Misc/AssertionMacros.h" #include "Misc/Optional.h" #include "Templates/SharedPointer.h" #include "UObject/ObjectMacros.h" #include "UObject/ObjectPtr.h" #include "UObject/UObjectGlobals.h" #include "EdModeInteractiveToolsContext.generated.h" class FCanvas; class FEdMode; class FEditorModeTools; class FEditorViewportClient; class FPrimitiveDrawInterface; class FSceneView; class FViewport; class FViewportClient; class ILevelEditor; class IToolsContextQueriesAPI; class IToolsContextRenderAPI; class IToolsContextTransactionsAPI; class UDragToolsBehaviorSource; class UEdModeInteractiveToolsContext; class UGizmoViewContext; class UInputRouter; class UInteractiveToolBuilder; class UMaterialInterface; class UObject; class USelection; class UTypedElementSelectionSet; struct FToolBuilderState; /** * UEditorInteractiveToolsContext is an extension/adapter of an InteractiveToolsContext designed * for use in the UE Editor. Currently this implementation assumes that it is created by a * Mode Manager (FEditorModeTools), and that the Mode Manager will call various API functions * like Render() and Tick() when necessary. * * * allows it to be easily embedded inside an FEdMode. A set of functions are provided which can be * called from the FEdMode functions of the same name. These will handle the data type * conversions and forwarding calls necessary to operate the ToolsContext */ UCLASS(Transient) class UNREALED_API UEditorInteractiveToolsContext : public UInteractiveToolsContext { GENERATED_BODY() public: UEditorInteractiveToolsContext(); /** * Initialize a new ToolsContext for an EdMode owned by the given InEditorModeManager * @param */ void InitializeContextWithEditorModeManager(FEditorModeTools* InEditorModeManager, UInputRouter* UseInputRouter = nullptr); /** Shutdown ToolsContext and clean up any connections/etc */ virtual void ShutdownContext(); // default behavior is to accept active tool virtual void TerminateActiveToolsOnPIEStart(); // default behavior is to accept active tool virtual void TerminateActiveToolsOnSaveWorld(); // default behavior is to cancel active tool virtual void TerminateActiveToolsOnWorldTearDown(); // default behavior is to cancel active tool virtual void TerminateActiveToolsOnLevelChange(); FEditorModeTools* GetParentEditorModeManager() const { return EditorModeManager; } IToolsContextQueriesAPI* GetQueriesAPI() const { return QueriesAPI; } IToolsContextTransactionsAPI* GetTransactionAPI() const { return TransactionAPI; } /** Call this to notify the Editor that the viewports this ToolsContext is related to may need a repaint, ie during interactive tool usage */ virtual void PostInvalidation(); // UObject Interface virtual UWorld* GetWorld() const override; // call functions of the same name on the ToolManager and GizmoManager virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime); virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI); virtual void DrawHUD(FViewportClient* ViewportClient,FViewport* Viewport,const FSceneView* View, FCanvas* Canvas); // These delegates can be used to hook into the Render() / DrawHUD() / Tick() calls above. In particular, non-legacy UEdMode's // don't normally receive Render() and DrawHUD() calls from the mode manager, but can attach to these. DECLARE_MULTICAST_DELEGATE_OneParam(FOnRender, IToolsContextRenderAPI* RenderAPI); FOnRender OnRender; DECLARE_MULTICAST_DELEGATE_TwoParams(FOnDrawHUD, FCanvas* Canvas, IToolsContextRenderAPI* RenderAPI); FOnDrawHUD OnDrawHUD; DECLARE_MULTICAST_DELEGATE_OneParam(FOnTick, float DeltaTime); FOnTick OnTick; /** @return true if selected actors/components can be deleted */ virtual bool ProcessEditDelete(); // // Utility functions useful for hooking up to UICommand/etc // virtual bool CanStartTool(const FString ToolTypeIdentifier) const; virtual bool HasActiveTool() const; virtual FString GetActiveToolName() const; virtual bool ActiveToolHasAccept() const; virtual bool CanAcceptActiveTool() const; virtual bool CanCancelActiveTool() const; virtual bool CanCompleteActiveTool() const; virtual void StartTool(const FString ToolTypeIdentifier); virtual void EndTool(EToolShutdownType ShutdownType); void Activate(); void Deactivate(); /** @return Ray into 3D scene at last mouse event */ virtual FRay GetLastWorldRay() const { check(false); return FRay(); } // // Configuration functions // /* * Configure whether ::Render() should early-out for HitProxy rendering passes. * If the Mode does not use HitProxy, and the Tools/Gizmos have expensive Render() calls, this can help with interactive performance. */ void SetEnableRenderingDuringHitProxyPass(bool bEnabled); /** @return true if HitProxy rendering will be allowed in ::Render() */ bool GetEnableRenderingDuringHitProxyPass() const { return bEnableRenderingDuringHitProxyPass; } /** * Configure whether Transform Gizmos created by the ITF (eg CombinedTransformGizmo) should prefer to show in 'Combined' mode. * If this is disabled, the Gizmo should respect the active Editor Gizmo setting (eg in the Level Viewport) */ void SetForceCombinedGizmoMode(bool bEnabled); /** @return true if Force Combined Gizmo mode is Enabled */ bool GetForceCombinedGizmoModeEnabled() const { return bForceCombinedGizmoMode; } /** * Configure whether Transform Gizmos created by the ITF (eg CombinedTransformGizmo) should, when in World coordinate system, * snap to an Absolute world-aligned grid, or snap Relative to the initial position of any particular gizmo transform. * Relative is the default and is also the behavior of the standard UE Gizmo. */ void SetAbsoluteWorldSnappingEnabled(bool bEnabled); /** @return true if Absolute World Snapping mode is Enabled */ bool GetAbsoluteWorldSnappingEnabled() const { return bEnableAbsoluteWorldSnapping; } /* * Configure whether tools should shutdown when entering PIE. * By default, the context will shut down any active tools on PIE start. * Setting this to true will prevent this, but the system makes no promises about whether tools work properly across PIE start/run/shutdown. * Therefore, do not use this if you do not know for certain that it is safe to persist tools across PIE start in your situation. */ void SetDeactivateToolsOnPIEStart(bool bDeactivateTools); /** @return true if tools should be deactivated when PIE is started */ bool GetDeactivateToolsOnPIEStart() const { return bDeactivateOnPIEStart; } /* * Configure whether tools should shutdown when the world is saving. * By default, the context will shut down any active tools on save. * Setting this to true will prevent this, but the system makes no promises about whether tools work properly across a save. * Therefore, do not use this if you do not know for certain that it is safe to persist tools across a save in your situation. */ void SetDeactivateToolsOnSaveWorld(bool bDeactivateTools); /** @return true if tools should be deactivated when save is started */ bool GetDeactivateToolsOnSaveWorld() const { return bDeactivateOnSaveWorld; } protected: // we hide these virtual void Initialize(IToolsContextQueriesAPI* QueriesAPI, IToolsContextTransactionsAPI* TransactionsAPI) override; virtual void Shutdown() override; virtual void DeactivateActiveTool(EToolSide WhichSide, EToolShutdownType ShutdownType); virtual void DeactivateAllActiveTools(EToolShutdownType ShutdownType); public: UPROPERTY() TObjectPtr StandardVertexColorMaterial; protected: // EdMode implementation of InteractiveToolFramework APIs - see ToolContextInterfaces.h IToolsContextQueriesAPI* QueriesAPI; IToolsContextTransactionsAPI* TransactionAPI; // Tools need to be able to Invalidate the view, in case it is not Realtime. // Currently we do this very aggressively, and also force Realtime to be on, but in general we should be able to rely on Invalidation. // However there are multiple Views and we do not want to Invalidate immediately, so we store a timestamp for each // ViewportClient, and invalidate it when we see it if it's timestamp is out-of-date. // (In theory this map will continually grow as new Viewports are created...) TMap InvalidationMap; // current invalidation timestamp, incremented by invalidation calls int32 InvalidationTimestamp = 0; // An object in which we save the current scene view information that gizmos can use on the game thread // to figure out how big the gizmo is for hit testing. Lives in the context store, but we keep a pointer here // to avoid having to look for it. UGizmoViewContext* GizmoViewContext = nullptr; // Utility function to convert viewport x/y from mouse events (and others?) into scene ray. // Copy-pasted from other Editor code, seems kind of expensive? static FRay GetRayFromMousePos(FEditorViewportClient* ViewportClient, FViewport* Viewport, int MouseX, int MouseY); // editor UI state that we set before starting tool and when exiting tool // Currently disabling anti-aliasing during active Tools because it causes PDI flickering void SetEditorStateForTool(); void RestoreEditorState(); void OnToolEnded(UInteractiveToolManager* InToolManager, UInteractiveTool* InEndedTool); void OnToolPostBuild(UInteractiveToolManager* InToolManager, EToolSide InSide, UInteractiveTool* InBuiltTool, UInteractiveToolBuilder* InToolBuilder, const FToolBuilderState& ToolState); TOptional PendingToolToStart = {}; TOptional PendingToolShutdownType = {}; private: FEditorModeTools* EditorModeManager = nullptr; // currently defaulting to enabled as FEdModes generally assume this, and in most cases hitproxy pass is not expensive. bool bEnableRenderingDuringHitProxyPass = true; bool bForceCombinedGizmoMode = false; bool bEnableAbsoluteWorldSnapping = false; bool bDeactivateOnPIEStart = true; bool bDeactivateOnSaveWorld = true; bool bIsActive = false; }; /** * UModeManagerInteractiveToolsContext extends UEditorInteractiveToolsContext with various functions for handling * device (mouse) input. These functions are currently called by the EdMode Manager (FEditorModeTools). */ UCLASS(Transient) class UNREALED_API UModeManagerInteractiveToolsContext : public UEditorInteractiveToolsContext { GENERATED_BODY() // // UEditorInteractiveToolsContext API implementations that also forward calls to any child EdMode ToolsContexts // public: virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime) override; virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override; virtual void DrawHUD(FViewportClient* ViewportClient,FViewport* Viewport,const FSceneView* View, FCanvas* Canvas) override; virtual bool ProcessEditDelete() override; // // Input handling, these functions forward ViewportClient events to the UInputRouter // public: bool InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event); /** * This updates internal state like InputKey, but doesn't route the results to the input router. * Use this if the input is captured by some higher system, to avoid this class from having an * incorrect view of e.g. the mouse state because it did not receive a mouse release event. */ void UpdateStateWithoutRoutingInputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event); bool MouseEnter(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y); bool MouseLeave(FEditorViewportClient* ViewportClient, FViewport* Viewport); bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y); bool LostFocus(FEditorViewportClient* InViewportClient, FViewport* Viewport) const; bool StartTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport); bool CapturedMouseMove(FEditorViewportClient* InViewportClient, FViewport* InViewport, int32 InMouseX, int32 InMouseY); bool EndTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport); /** @return True if the context has overriden the cursor style, false if not. */ bool GetCursor(EMouseCursor::Type& OutCursor) const; /** @return Ray into 3D scene at last mouse event */ virtual FRay GetLastWorldRay() const override; protected: virtual void DeactivateAllActiveTools(EToolShutdownType ShutdownType) override; virtual void Initialize(IToolsContextQueriesAPI* QueriesAPIIn, IToolsContextTransactionsAPI* TransactionsAPIIn) override; virtual void Shutdown() override; /** Input event instance used to keep track of various button states, etc, that we cannot directly query on-demand */ FInputDeviceState CurrentMouseState; // called when PIE is about to start, shuts down active tools FDelegateHandle BeginPIEDelegateHandle; // called before a Save starts. This currently shuts down active tools. FDelegateHandle PreSaveWorldDelegateHandle; // called when a map is changed FDelegateHandle WorldTearDownDelegateHandle; // called when viewport clients change FDelegateHandle ViewportClientListChangedHandle; private: bool bIsTrackingMouse; protected: UPROPERTY() TArray> EdModeToolsContexts; public: /** * Create and initialize a new EdMode-level ToolsContext derived from the ModeManager ToolsContext. * The EdMode ToolsContext does not have it's own InputRouter, it shares the InputRouter with the ModeManager ToolsContext. * The ModeManager ToolsContext keeps track of these derived ToolsContext's and automatically Tick()'s them/etc. * When the child ToolsContext is shut down, OnChildEdModeToolsContextShutdown() must be called to clean up * @return new ToolsContext */ UEdModeInteractiveToolsContext* CreateNewChildEdModeToolsContext(); /** * Call to add a child EdMode ToolsContext created using the above function * @return true if child was added */ bool OnChildEdModeActivated(UEdModeInteractiveToolsContext* ChildToolsContext); /** * Call to release a child EdMode ToolsContext created using the above function * @return true if child was found and removed */ bool OnChildEdModeDeactivated(UEdModeInteractiveToolsContext* ChildToolsContext); /** * Mark Editor Modes Tools for Drag Tools Support */ void SetDragToolsEnabled(bool bInEnabled) { bUsesDragTools = bInEnabled; } /** * Are Drag Tools supported? e.g. Marquee Select * Enable/Disable support by using SetDragToolsEnabled(...) */ bool UsesDragTools() const { return bUsesDragTools; }; protected: /** * Activating Drag Tools if current Mode Manager requires them */ void RegisterDragTools(); /** * Deactivating Drag Tools */ void UnregisterDragTools(); /** * Responding to Mode Changes in order to deactivate/activate Drag Tools accordingly */ void OnEditorModeChanged(const FEditorModeID& InModeID, bool bInIsEnteringMode); //~Begin ITF Alt processing Bypass /** * These functions are declared protected to avoid having to deal with deprecation later on. * The Alt modifier bypass will be removed once a complete switch to ITF happens */ /** Enable/Disable Alt modifier bypass */ void SetBypassAltModifier(bool bInBypassAltModifier); /** Is this Interactive Tools Context skipping Alt processing? */ bool BypassAltModifier() const { return bBypassAltModifier; } /** Should we skip processing Alt modifier inputs? See UModeManagerInteractiveToolsContext::InputKey */ bool bBypassAltModifier = true; //~End ITF Alt processing Bypass /** * */ UPROPERTY(Transient) TObjectPtr DragToolsBehaviorSource; bool bUsesDragTools = false; }; /** * UEdModeInteractiveToolsContext is an UEditorInteractiveToolsContext intended for use/lifetime in the context of a UEdMode. * This ITC subclass is dependent on a UModeManagerInteractiveToolsContext to provide an InputRouter. */ UCLASS(Transient) class UNREALED_API UEdModeInteractiveToolsContext : public UEditorInteractiveToolsContext { friend class UModeManagerInteractiveToolsContext; GENERATED_BODY() public: /** * Initialize a new EdModeToolsContext that is derived from a ModeManagerToolsContext. * This new ToolsContext will not have it's own InputRouter, it will share the InputRouter with the ModeManagerToolsContext */ void InitializeContextFromModeManagerContext(UModeManagerInteractiveToolsContext* ModeManagerToolsContext); /** @return Ray into 3D scene at last mouse event */ virtual FRay GetLastWorldRay() const override; protected: UPROPERTY() TObjectPtr ParentModeManagerToolsContext = nullptr; };