Files
UnrealEngineUWP/Engine/Source/Editor/SubobjectDataInterface/Public/SubobjectData.h
ben hoffman 61908bed8a Create the Subobject Data interface module. This provides an API that can be used to manipulate Subobjects such as adding/removing components, reparenting, and changing the scene heirarchy.
The SubobjectDataSubsystem is how users can manipulate a given object within Blueprints or Python scripting. Given a Uobject instance in a level or a UBlueprint asset, you can use GatherSubobjectData to get get an array of handles that you can use to manipulate that subobject.

This is what the new SubobjectEditor (Previously the SCS editor) will be using instead of having all its logic within slate code.

#rb marc.audy
#jira UE-64131
#preflight 6082ce4f8de3a60001cf6af8
#preflight 6082d84a92d7e700019f53e0

[CL 16104548 by ben hoffman in ue5-main branch]
2021-04-23 11:01:15 -04:00

303 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Templates/SharedPointer.h"
#include "SubobjectDataHandle.h"
#include "Components/ActorComponent.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Engine/SCS_Node.h" // #TODO_BH We need to remove this when the actual subobject refactor happens
#include "SubobjectData.generated.h"
class UBlueprint;
/**
* A struct that represents a single subobject. This can be anything, but are
* most commonly components attached to an actor instance or blueprint. Keeps track
* of the handles to its parent object and any child that it has.
*
* If you wish to modify a subobject, use the SubobjectDataSubsystem.
*/
USTRUCT(BlueprintType)
struct SUBOBJECTDATAINTERFACE_API FSubobjectData
{
friend class USubobjectDataSubsystem;
public:
GENERATED_BODY()
FSubobjectData();
FSubobjectData(UObject* ContextObject, const FSubobjectDataHandle& ParentHandle);
virtual ~FSubobjectData();
/** True if this subobject's handle is valid */
bool IsValid() const { return Handle.IsValid(); }
/**
* @return Whether or not we can edit properties for this subobject
*/
virtual bool CanEdit() const;
/**
* @return Whether or not this object represents a subobject that can be deleted
*/
virtual bool CanDelete() const;
/**
* @return Whether or not this object represents a subobject that can be duplicated
*/
virtual bool CanDuplicate() const;
/**
* @return Whether or not this object represents a subobject that can be copied
*/
virtual bool CanCopy() const;
/**
* @return Whether or not this object represents a subobject that can
* be reparented to other subobjects based on its context.
*/
virtual bool CanReparent() const;
/**
* @return Whether or not this object represents a subobject that can
* be renamed.
*/
virtual bool CanRename() const;
/**
* @return A read-only reference to the object represented by this node.
*/
inline const UObject* GetObject(bool bEvenIfPendingKill = false) const { return WeakObjectPtr.Get(bEvenIfPendingKill); }
/**
* @param InBlueprint The Blueprint in which the object will be edited.
*
* @note May not be the same as the value returned by GetObject().
* @return A reference to the object represented by this node that can be modified within the given Blueprint.
*/
const UObject* GetObjectForBlueprint(UBlueprint* Blueprint) const;
template<class T>
inline const T* GetObjectForBlueprint(UBlueprint* Blueprint) const
{
return Cast<T>(GetObjectForBlueprint(Blueprint));
}
/**
* @param bEvenIfPendingKill If false, nullptr will be returned if the cached object instance is pending kill.
* If true, it will be returned regardless (this is used for recaching the object if the objects
* have been reinstanced following construction script execution).
*
* @note Deliberately non-virtual, for performance reasons.
* @return A read-only reference to the object represented by this node.
*/
template<class T>
inline const T* GetObject(bool bEvenIfPendingKill = false) const
{
return Cast<T>(WeakObjectPtr.Get(bEvenIfPendingKill));
}
/**
* @param bEvenIfPendingKill If false, nullptr will be returned if the cached component template is pending kill.
* If true, it will be returned regardless (this is used for recaching the component template if the objects
* have been reinstanced following construction script execution).
*
* @note Deliberately non-virtual, for performance reasons.
* @warning This will not return the right component for components overridden by the inherited component handler, you need to call GetOrCreateEditableComponentTemplate instead
* @return The component template or instance represented by this node, if it's a component node.
*/
inline const UActorComponent* GetComponentTemplate(bool bEvenIfPendingKill = false) const
{
return Cast<UActorComponent>(WeakObjectPtr.Get(bEvenIfPendingKill));
}
inline const UActorComponent* FindComponentInstanceInActor(const AActor* InActor) const { return FindMutableComponentInstanceInActor(InActor); }
UBlueprint* GetBlueprint() const;
virtual bool IsInstancedComponent() const;
virtual bool IsInstancedActor() const;
virtual bool IsNativeComponent() const;
virtual bool IsInheritedComponent() const;
virtual bool IsSceneComponent() const;
virtual bool IsRootComponent() const;
virtual bool IsDefaultSceneRoot() const;
/* Returns true if this subobject is a component. */
virtual bool IsComponent() const;
virtual bool IsChildActor() const;
virtual bool IsRootActor() const;
virtual bool IsActor() const;
/** True if this subobject is an instance of an inherited component (its owner is an instanced actor) */
virtual bool IsInstancedInheritedComponent() const;
bool IsAttachedTo(const FSubobjectDataHandle& InHandle) const;
virtual FString GetDisplayString(bool bShowNativeComponentNames = true) const;
/**
* Returns a string that gives details about this subobject. For Inherited
* components it will return "(Inherited)". For native components
* this function will return "(NativeComponentName) (Inherited)"
*/
virtual FText GetDisplayNameContextModifiers(bool bShowNativeComponentNames = true) const;
virtual FText GetDisplayName() const;
virtual FName GetVariableName() const;
// Sockets for attaching in the viewport
FText GetSocketName() const;
FName GetSocketFName() const;
bool HasValidSocket() const;
void SetSocketName(FName InNewName);
void SetupAttachment(FName SocketName, const FSubobjectDataHandle& AttachParentHandle = FSubobjectDataHandle::InvalidHandle);
FSubobjectDataHandle GetHandle() const { return Handle; }
FSubobjectDataHandle GetParentHandle() const { return ParentObjectHandle; }
FSubobjectData* GetParentData() const { return ParentObjectHandle.GetData(); }
bool HasParent() const { return ParentObjectHandle.IsValid(); }
const TArray<FSubobjectDataHandle>& GetChildrenHandles() const { return ChildrenHandles; }
/** Walks the parent heirarchy and returns the root subobject in this case */
FSubobjectDataHandle GetRootSubobject() const;
// Return true if the given handle is a child of this subobject
bool HasChild(const FSubobjectDataHandle& ChildHandle) const;
// Attempt to find the given handle in out child array and return a pointer to it
FSubobjectDataHandle FindChild(const FSubobjectDataHandle& ChildHandle) const;
FSubobjectDataHandle FindChildByObject(UObject* ContextObject) const;
/** Get the asset name of this subobject from the asset brokerage */
FText GetAssetName() const;
/** Get the asset path of this subobject from the asset brokerage */
FText GetAssetPath() const;
/** Returns true if the asset this object represents is visible */
bool IsAssetVisible() const;
/**
* Retrieves tooltip text describing the specified component's mobility.
*
* @returns An FText object containing a description of the component's mobility
*/
FText GetMobilityToolTipText() const;
/**
* Retrieves a tooltip text describing if the component is marked Editor only or not
*
* @returns An FText object containing a description of if the component is marked Editor only or not
*/
FText GetComponentEditorOnlyTooltipText() const;
/**
* Retrieves tooltip text describing where the component was first introduced (for inherited components).
*
* @returns An FText object containing a description of when the component was first introduced
*/
FText GetIntroducedInToolTipText() const;
virtual FText GetActorDisplayText() const;
FLinearColor GetColorTintForIcon() const;
protected:
/**
* Add the given subobject handle as a child of this.
* NOTE: This does NOT do any actual manipulation of the subobject structure.
* This only updates this subobject to have the correct handles in it's child structure.
* Actual manipulation of subobjects should be done via the SubobjectSubsystem.
*/
bool AddChildHandleOnly(const FSubobjectDataHandle& InHandle);
/**
* Add the given subobject handle as a child of this.
* * NOTE: This does NOT do any actual manipulation of the subobject structure.
* This only updates this subobject to have the correct handles in it's child structure.
* Actual manipulation of subobjects should be done via the SubobjectSubsystem.
*/
bool RemoveChildHandleOnly(const FSubobjectDataHandle& InHandle);
void SetParentHandle(const FSubobjectDataHandle& NewParentHandle) { ParentObjectHandle = NewParentHandle; }
void ClearParentHandle() { ParentObjectHandle = FSubobjectDataHandle::InvalidHandle; }
// Weak ptr to the object instance represented by this node (e.g. component template)
UPROPERTY()
TWeakObjectPtr<UObject> WeakObjectPtr;
// Handle that will have a globally unique ID when it is constructed (i.e. when this is constructed)
UPROPERTY()
FSubobjectDataHandle Handle;
// Handle to this object's parent. If this is invalid, then it is the root subobject.
UPROPERTY()
FSubobjectDataHandle ParentObjectHandle;
// Set of child subobjects that use this subobject as a parent
UPROPERTY()
TArray<FSubobjectDataHandle> ChildrenHandles;
////////////////////////////////////////////
// Muteable accessors for this subobject to be used by the subsystem
inline UObject* GetMutableObject(bool bEvenIfPendingKill = false) const { return WeakObjectPtr.Get(bEvenIfPendingKill); }
// If this subobject is an actor, then return a pointer to that. Otherwise, search for
// the owning actor on this component and return it.
AActor* GetMutableActorContext();
template<class T>
inline T* GetMutableObject(bool bEvenIfPendingKill = false) const
{
return Cast<T>(WeakObjectPtr.Get(bEvenIfPendingKill));
}
template<class T>
inline T* GetMutableObjectForBlueprint(UBlueprint* Blueprint) const
{
return const_cast<T*>(GetObjectForBlueprint<T>(Blueprint));
}
inline UActorComponent* GetMutableComponentTemplate(bool bEvenIfPendingKill = false) const
{
return Cast<UActorComponent>(WeakObjectPtr.Get(bEvenIfPendingKill));
}
UActorComponent* FindMutableComponentInstanceInActor(const AActor* InActor) const;
////////////////////////////////////////////////////
// Anything related to an SCS node will be changed with an upcoming refactor and should remain private
protected:
// Tries to find a SCS node that was likely responsible for creating the specified instance component. Note: This is not always possible to do!
static USCS_Node* FindSCSNodeForInstance(const UActorComponent* InstanceComponent, UClass* ClassToSearch);
virtual USCS_Node* GetSCSNode(bool bEvenIfPendingKill = false) const;
/**
* Attempts to set the SCS node member variable based on the WeakObjectPtr.
* If the weak obj ptr _is_ an USCS_Node type, then we have to change the component
* template.
*
* If this is an instanced component, then we can find the scs node via FindSCSNodeForInstance.
*
* @return True if an SCS node has been set
*/
bool AttemptToSetSCSNode();
// For now, we need a pointer to the SCS node in order to have the correct component template
// and to be able to get the UBlueprint* from components. This will only be set upon construction
// if the given Object Context is an USCS_Node. The context object will be set to its template.
TWeakObjectPtr<USCS_Node> SCSNodePtr;
/**
* True if this SCS node's blueprint is a child of another Blueprint-generated class,
* which means that we must use the InheritableComponentHandler
*/
virtual bool IsInheritedSCSNode() const;
};