2019-12-26 14:45:42 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-04-04 16:11:44 -04:00
# pragma once
# include "CoreMinimal.h"
# include "UObject/Object.h"
# include "InteractiveTool.h"
# include "InteractiveToolBuilder.h"
# include "InputRouter.h"
2019-09-23 21:52:12 -04:00
# include "InteractiveToolChange.h"
2019-04-04 16:11:44 -04:00
# include "ToolContextInterfaces.h"
# include "InteractiveToolManager.generated.h"
2020-11-13 14:07:30 -04:00
class UInteractiveToolStorableSelection ;
2021-05-05 12:42:29 -04:00
class UContextObjectStore ;
2019-04-04 16:11:44 -04:00
/** A Tool can be activated on a particular input device, currently identified by a "side" */
UENUM ( )
enum class EToolSide
{
/** Left-hand Tool, also used for Mouse */
Left = 1 ,
2019-04-27 20:21:05 -04:00
Mouse = 1 ,
2019-04-04 16:11:44 -04:00
/** Right-hand Tool*/
2019-04-27 20:21:05 -04:00
Right = 2 ,
2019-04-04 16:11:44 -04:00
} ;
2020-02-22 17:01:16 -05:00
/**
* UInteractiveToolManager can emit change events for the active tool in various ways .
* This allows different modes to control how tools activate / deactivate on undo / redo , which is necessary
* because some modes ( eg Modeling Mode ) do not support redo " into " a Tool , while others require it ( like Paint Mode )
*/
UENUM ( )
enum class EToolChangeTrackingMode
{
/** Do not emit any Active Tool change events */
NoChangeTracking = 1 ,
/** When Activating a new Tool, emit a change that will cancel/deactivate that Tool on Undo, but not reactivate it on Redo */
UndoToExit = 2 ,
/** Full change tracking of active Tool. Note that on Activation when an existing Tool is auto-shutdown, two separate FChanges are emitted, wrapped in a single Transaction */
FullUndoRedo = 3
} ;
2019-04-04 16:11:44 -04:00
/**
* UInteractiveToolManager allows users of the tools framework to create and operate Tool instances .
* For each Tool , a ( string , ToolBuilder ) pair is registered with the ToolManager .
* Tools can then be activated via the string identifier .
*
* Currently a single Tool can be active for each input device . So for mouse input a single
* Tool is available and effectively a lightweight mode . The mouse uses the " Left " tool slot .
*
* For VR controllers and touch input , a " Left " and " Right " tool can be active at the same time .
* @ todo this is not fully supported yet
*
* Tools are not directly created . Use SelectActiveToolType ( side , string ) to set the active ToolBuilder
* on a given side , and then use ActivateTool ( ) to create the new Tool instance .
*
*/
UCLASS ( Transient )
2019-09-23 16:14:03 -04:00
class INTERACTIVETOOLSFRAMEWORK_API UInteractiveToolManager : public UObject , public IToolContextTransactionProvider
2019-04-04 16:11:44 -04:00
{
GENERATED_BODY ( )
protected :
friend class UInteractiveToolsContext ; // to call Initialize/Shutdown
UInteractiveToolManager ( ) ;
/** Initialize the ToolManager with the necessary Context-level state. UInteractiveToolsContext calls this, you should not. */
virtual void Initialize ( IToolsContextQueriesAPI * QueriesAPI , IToolsContextTransactionsAPI * TransactionsAPI , UInputRouter * InputRouter ) ;
/** Shutdown the ToolManager. Called by UInteractiveToolsContext. */
virtual void Shutdown ( ) ;
2021-03-31 08:34:48 -04:00
/** Called immediately after a tool is built. Broadcasts OnToolPostBuild. */
virtual void DoPostBuild ( EToolSide Side , UInteractiveTool * InBuiltTool , UInteractiveToolBuilder * InToolBuilder , const FToolBuilderState & InBuilderState ) ;
/** Called immediately after a tool's Setup is called. Broadcasts OnToolPostSetup. */
2021-08-03 20:16:34 -04:00
virtual void DoPostSetup ( EToolSide Side , UInteractiveTool * InInteractiveTool , UInteractiveToolBuilder * InToolBuilder , const FToolBuilderState & InBuilderState ) ;
2021-03-31 08:34:48 -04:00
2019-04-04 16:11:44 -04:00
public :
2020-03-19 10:59:25 -04:00
/**
* @ return true if ToolManager is currently active , ie between Initialize ( ) and Shutdown ( )
*/
bool IsActive ( ) const { return bIsActive ; }
2019-04-04 16:11:44 -04:00
//
// Tool registration and Current Tool state
//
/**
* Register a new ToolBuilder
* @ param Identifier string used to identify this Builder
* @ param Builder new ToolBuilder instance
*/
virtual void RegisterToolType ( const FString & Identifier , UInteractiveToolBuilder * Builder ) ;
2020-09-02 15:43:58 -04:00
/**
* Unregisters a ToolBuilder
* @ param Identifier string used to identify this Builder
* @ param Builder new ToolBuilder instance
*/
virtual void UnregisterToolType ( const FString & Identifier ) ;
2019-04-04 16:11:44 -04:00
/**
* Set active ToolBuilder for a ToolSide via string identifier
* @ param Side which " side " should we set this Builder on
* @ param Identifier name of ToolBuilder that was passed to RegisterToolType ( )
*/
virtual bool SelectActiveToolType ( EToolSide Side , const FString & Identifier ) ;
/**
* Check if a named Tool type can currently be activated on the given ToolSide
* @ param Side which " side " you would like to active the tool on
* @ param Identifier string name of the Tool type
* @ return true if the Tool type could be activated
*/
2020-10-08 18:56:55 -04:00
virtual bool CanActivateTool ( EToolSide eSide , FString Identifier ) ;
2019-04-04 16:11:44 -04:00
/**
* Try to activate a new Tool instance on the given Side
* @ param Side which " side " you would like to active the tool on
* @ return true if a new Tool instance was created and initialized
*/
virtual bool ActivateTool ( EToolSide Side ) ;
/**
* Check if there is an active Tool on the given Side
* @ param Side which Side to check
* @ return true if there is an active Tool on that side
*/
2019-04-27 20:21:05 -04:00
virtual bool HasActiveTool ( EToolSide Side ) const ;
/**
* @ return true if there are any active tools
*/
virtual bool HasAnyActiveTool ( ) const ;
2019-04-04 16:11:44 -04:00
/**
* Get pointer to active Tool on a given side
* @ param Side which Side is being requested
* @ return pointer to Tool instance active on that Side , or nullptr if no such Tool exists
*/
virtual UInteractiveTool * GetActiveTool ( EToolSide Side ) ;
2019-10-24 16:55:47 -04:00
/**
* Get pointer to active Tool Builder on a given side
* @ param Side which Side is being requested
* @ return pointer to Tool Builder instance active on that Side , or nullptr if no such ToolBuilder exists
*/
virtual UInteractiveToolBuilder * GetActiveToolBuilder ( EToolSide Side ) ;
2020-03-27 14:26:54 -04:00
/**
* Get name of registered ToolBuilder that created active tool for given side , or empty string if no tool is active
* @ param Side which Side is being requested
* @ return name of tool , or empty string if no tool is active
*/
virtual FString GetActiveToolName ( EToolSide Side ) ;
2019-04-04 16:11:44 -04:00
/**
* Check if an active Tool on the given Side can be Accepted in its current state
* @ param Side which Side to check
* @ return true if there is an active Tool and it returns true from HasAccept ( ) and CanAccept ( )
*/
virtual bool CanAcceptActiveTool ( EToolSide Side ) ;
/**
* Check if an active Tool on the given Side can be Canceled
* @ param Side which Side to check
* @ return true if there is an active Tool and it returns true from HasCancel ( )
*/
virtual bool CanCancelActiveTool ( EToolSide Side ) ;
/**
* Shut down an active Tool on the given side
* @ param Side which " side " you would like to shut down
* @ param ShutdownType how should the tool be terminated ( eg Accept / Cancel )
*/
virtual void DeactivateTool ( EToolSide Side , EToolShutdownType ShutdownType ) ;
2020-02-22 17:01:16 -05:00
/**
* Configure how tool changes emit change events . See EToolChangeTrackingMode for details .
*/
virtual void ConfigureChangeTrackingMode ( EToolChangeTrackingMode ChangeMode ) ;
2019-04-04 16:11:44 -04:00
//
// Functions that Tools can call to interact with Transactions API
//
2019-04-08 15:37:19 -04:00
/** Post a message via the Transactions API */
2019-09-16 14:06:18 -04:00
virtual void DisplayMessage ( const FText & Message , EToolMessageLevel Level ) ;
2019-04-08 15:37:19 -04:00
2019-04-04 16:11:44 -04:00
/** Request an Invalidation via the Transactions API (ie to cause a repaint, etc) */
virtual void PostInvalidation ( ) ;
/**
* Request that the Context open a Transaction , whatever that means to the current Context
* @ param Description text description of this transaction ( this is the string that appears on undo / redo in the UE Editor )
*/
virtual void BeginUndoTransaction ( const FText & Description ) ;
/** Request that the Context close and commit the open Transaction */
virtual void EndUndoTransaction ( ) ;
/**
* Forward an FChange object to the Context
* @ param TargetObject the object that the FChange applies to
* @ param Change the change object that the Context should insert into the transaction history
* @ param Description text description of this change ( this is the string that appears on undo / redo in the UE Editor )
*/
2019-09-23 21:52:12 -04:00
virtual void EmitObjectChange ( UObject * TargetObject , TUniquePtr < FToolCommandChange > Change , const FText & Description ) ;
2019-04-04 16:11:44 -04:00
2019-05-06 15:41:42 -04:00
/**
* Forward an FChange object to the Context
*/
virtual bool RequestSelectionChange ( const FSelectedOjectsChangeList & SelectionChange ) ;
2019-04-04 16:11:44 -04:00
//
// State control (@todo: have the Context call these? not safe for anyone to call)
//
/** Tick any active Tools. Called by UInteractiveToolsContext */
virtual void Tick ( float DeltaTime ) ;
/** Render any active Tools. Called by UInteractiveToolsContext. */
virtual void Render ( IToolsContextRenderAPI * RenderAPI ) ;
2020-08-31 16:54:59 -04:00
/** Let active Tools do their screen space drawing. Called by UInteractiveToolsContext. */
2020-09-18 09:25:18 -04:00
virtual void DrawHUD ( FCanvas * Canvas , IToolsContextRenderAPI * RenderAPI ) ;
2020-08-31 16:54:59 -04:00
2019-04-04 16:11:44 -04:00
2019-05-06 15:41:42 -04:00
//
// access to APIs, etc
//
/** @return current IToolsContextQueriesAPI */
virtual IToolsContextQueriesAPI * GetContextQueriesAPI ( ) { return QueriesAPI ; }
2021-02-08 17:13:32 -04:00
/** @return current IToolsContextTransactionsAPI */
virtual IToolsContextTransactionsAPI * GetContextTransactionsAPI ( ) final { return TransactionsAPI ; }
2019-09-10 12:01:07 -04:00
UInteractiveGizmoManager * GetPairedGizmoManager ( ) ;
2019-05-06 15:41:42 -04:00
2021-05-05 12:42:29 -04:00
/**
* @ return the context object store from the owning tools context .
*/
UContextObjectStore * GetContextObjectStore ( ) const ;
2019-05-06 15:41:42 -04:00
2019-04-04 16:11:44 -04:00
public :
/** Currently-active Left Tool, or null if no Tool is active */
UPROPERTY ( )
2021-01-27 17:40:25 -04:00
TObjectPtr < UInteractiveTool > ActiveLeftTool ;
2019-04-04 16:11:44 -04:00
/** Currently-active Right Tool, or null if no Tool is active */
UPROPERTY ( )
2021-01-27 17:40:25 -04:00
TObjectPtr < UInteractiveTool > ActiveRightTool ;
2019-04-04 16:11:44 -04:00
2019-05-06 15:41:42 -04:00
public :
DECLARE_MULTICAST_DELEGATE_TwoParams ( FToolManagerToolStartedSignature , UInteractiveToolManager * , UInteractiveTool * ) ;
FToolManagerToolStartedSignature OnToolStarted ;
2021-03-31 08:34:48 -04:00
DECLARE_MULTICAST_DELEGATE_FiveParams ( FToolManagerToolPostBuildSignature , UInteractiveToolManager * , EToolSide , UInteractiveTool * , UInteractiveToolBuilder * , const FToolBuilderState & ) ;
FToolManagerToolPostBuildSignature OnToolPostBuild ;
DECLARE_MULTICAST_DELEGATE_ThreeParams ( FToolManagerToolPostSetupSignature , UInteractiveToolManager * , EToolSide , UInteractiveTool * ) ;
FToolManagerToolPostSetupSignature OnToolPostSetup ;
2019-05-06 15:41:42 -04:00
DECLARE_MULTICAST_DELEGATE_TwoParams ( FToolManagerToolEndedSignature , UInteractiveToolManager * , UInteractiveTool * ) ;
FToolManagerToolEndedSignature OnToolEnded ;
2019-04-04 16:11:44 -04:00
protected :
/** Pointer to current Context-Queries implementation */
IToolsContextQueriesAPI * QueriesAPI ;
/** Pointer to current Transactions implementation */
IToolsContextTransactionsAPI * TransactionsAPI ;
/** Pointer to current InputRouter (Context owns this) */
UInputRouter * InputRouter ;
2020-03-19 10:59:25 -04:00
/** This flag is set to true on Initialize() and false on Shutdown(). */
bool bIsActive = false ;
2019-04-04 16:11:44 -04:00
/** Current set of named ToolBuilders */
UPROPERTY ( )
2021-01-27 17:40:25 -04:00
TMap < FString , TObjectPtr < UInteractiveToolBuilder > > ToolBuilders ;
2019-04-04 16:11:44 -04:00
/** Currently-active Left ToolBuilder */
2020-02-22 17:01:16 -05:00
FString ActiveLeftBuilderName ;
2019-04-04 16:11:44 -04:00
UInteractiveToolBuilder * ActiveLeftBuilder ;
/** Currently-active Right ToolBuilder */
2020-02-22 17:01:16 -05:00
FString ActiveRightBuilderName ;
2019-04-04 16:11:44 -04:00
UInteractiveToolBuilder * ActiveRightBuilder ;
2020-02-22 17:01:16 -05:00
EToolChangeTrackingMode ActiveToolChangeTrackingMode ;
FString ActiveLeftToolName ;
FString ActiveRightToolName ;
2020-11-13 14:07:30 -04:00
/**
* Tracks whether the last activated tool has made a request to store a tool selection .
* If it hasn ' t , we ' ll clear the currently stored tool selection . The reason for this is
* that we generally don ' t want to keep the old stored tool selection after a new tool
* invocation , and we don ' t want to rely on tools to be kind enough to clear it for us
* ( though it is better when they do , since they can bundle it into their own undo transaction ) .
*/
bool bActiveToolMadeSelectionStoreRequest = false ;
2020-02-22 17:01:16 -05:00
virtual bool ActivateToolInternal ( EToolSide Side ) ;
virtual void DeactivateToolInternal ( EToolSide Side , EToolShutdownType ShutdownType ) ;
friend class FBeginToolChange ;
friend class FActivateToolChange ;
2019-09-10 14:40:38 -04:00
} ;
/**
* FBeginToolChange is used by UInteractiveToolManager to back out of a Tool on Undo .
* No action is taken on Redo , ie we do not re - start the Tool on Redo .
*/
2019-09-23 21:52:12 -04:00
class INTERACTIVETOOLSFRAMEWORK_API FBeginToolChange : public FToolCommandChange
2019-09-10 14:40:38 -04:00
{
public :
virtual void Apply ( UObject * Object ) override ;
virtual void Revert ( UObject * Object ) override ;
virtual bool HasExpired ( UObject * Object ) const override ;
virtual FString ToString ( ) const override ;
} ;
2020-02-22 17:01:16 -05:00
/**
* FActivateToolChange is used by UInteractiveToolManager to change the active tool .
* This Change has two modes , either activating or deactivating .
*/
class INTERACTIVETOOLSFRAMEWORK_API FActivateToolChange : public FToolCommandChange
{
public :
EToolSide Side ;
FString ToolType ;
bool bIsDeactivate = false ;
EToolShutdownType ShutdownType ;
FActivateToolChange ( EToolSide SideIn , FString ToolTypeIn )
: Side ( SideIn ) , ToolType ( ToolTypeIn ) , bIsDeactivate ( false ) { }
FActivateToolChange ( EToolSide SideIn , FString ToolTypeIn , EToolShutdownType ShutdownTypeIn )
: Side ( SideIn ) , ToolType ( ToolTypeIn ) , bIsDeactivate ( true ) , ShutdownType ( ShutdownTypeIn ) { }
virtual void Apply ( UObject * Object ) override ;
virtual void Revert ( UObject * Object ) override ;
virtual bool HasExpired ( UObject * Object ) const override ;
virtual FString ToString ( ) const override ;
} ;
2019-09-10 14:40:38 -04:00
/**
* FToolChangeWrapperChange wraps an FChange emitted by an InteractiveTool , allowing
* us to Expire the change without each FChange implementation needing to handle this explicitly .
*/
2019-09-23 21:52:12 -04:00
class INTERACTIVETOOLSFRAMEWORK_API FToolChangeWrapperChange : public FToolCommandChange
2019-09-10 14:40:38 -04:00
{
public :
TWeakObjectPtr < UInteractiveToolManager > ToolManager ;
TWeakObjectPtr < UInteractiveTool > ActiveTool ;
2019-09-23 21:52:12 -04:00
TUniquePtr < FToolCommandChange > ToolChange ;
2019-09-10 14:40:38 -04:00
virtual void Apply ( UObject * Object ) override ;
virtual void Revert ( UObject * Object ) override ;
virtual bool HasExpired ( UObject * Object ) const override ;
virtual FString ToString ( ) const override ;
2020-08-31 16:54:59 -04:00
} ;