// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "IPropertyTypeCustomization.h" #include "IDetailCustomNodeBuilder.h" #include "IStructureDataProvider.h" #define UE_API STRUCTUTILSEDITOR_API class IAssetReferenceFilter; class IPropertyHandle; class IDetailGroup; class IDetailPropertyRow; class IPropertyHandle; class FStructOnScope; class SWidget; class SInstancedStructPicker; struct FInstancedStruct; class FInstancedStructProvider; /** * Type customization for FInstancedStruct. */ class FInstancedStructDetails : public IPropertyTypeCustomization { public: UE_API virtual ~FInstancedStructDetails() override; /** Makes a new instance of this detail layout class for a specific detail view requesting it */ static UE_API TSharedRef MakeInstance(); /** IPropertyTypeCustomization interface */ UE_API virtual void CustomizeHeader(TSharedRef StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; UE_API virtual void CustomizeChildren(TSharedRef StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; private: UE_API void OnCopyInstancedStruct() const; UE_API bool CanPasteInstancedStruct() const; UE_API void OnPasteInstancedStruct(); using FReplacementObjectMap = TMap; UE_API void OnObjectsReinstanced(const FReplacementObjectMap& ObjectMap); /** Handle to the struct property being edited */ TSharedPtr StructProperty; TSharedPtr StructPicker; TSharedPtr PropUtils; FDelegateHandle OnObjectsReinstancedHandle; }; /** * Node builder for FInstancedStruct children. * Expects property handle holding FInstancedStruct as input. * Can be used in a implementation of a IPropertyTypeCustomization CustomizeChildren() to display editable FInstancedStruct contents. * OnChildRowAdded() is called right after each property is added, which allows the property row to be customizable. * Child properties will be grouped if they 1) have "Category" metadata, and 2) have the "EnableCategories" metadata tag. */ class FInstancedStructDataDetails : public IDetailCustomNodeBuilder, public TSharedFromThis { public: UE_API FInstancedStructDataDetails(TSharedPtr InStructProperty); UE_API virtual ~FInstancedStructDataDetails() override; //~ Begin IDetailCustomNodeBuilder interface UE_API virtual void SetOnRebuildChildren(FSimpleDelegate InOnRegenerateChildren) override; UE_API virtual void GenerateHeaderRowContent(FDetailWidgetRow& NodeRow) override; UE_API virtual void GenerateChildContent(IDetailChildrenBuilder& ChildrenBuilder) override; UE_API virtual void Tick(float DeltaTime) override; virtual bool RequiresTick() const override { return true; } virtual bool InitiallyCollapsed() const override { return false; } UE_API virtual FName GetName() const override; //~ End IDetailCustomNodeBuilder interface // Called when a group is added, override to customize a group row. virtual void OnGroupRowAdded(IDetailGroup& GroupRow, int32 Level, const FString& Category) const {} // Called when a child is added, override to customize a child row. virtual void OnChildRowAdded(IDetailPropertyRow& ChildRow) {} protected: UE_API static TArray GetPropertyCategories(TSharedPtr PropertyHandle); UE_API virtual void AddChildRows(IDetailChildrenBuilder& ChildBuilder, const TArray>& ChildProperties); private: UE_API void OnUserDefinedStructReinstancedHandle(const class UUserDefinedStruct& Struct); /** Pre/Post change notifications for struct value changes */ UE_API void OnStructValuePreChange(); UE_API void OnStructValuePostChange(); UE_API void OnStructHandlePostChange(); /** Returns type of the instanced struct for each instance/object being edited. */ UE_API TArray> GetInstanceTypes() const; /** * Adds groups for the specified properties. One group is created for each unique category (from property metadata) that the properties have. * If a category is pipe-separated (eg, Foo|Bar), one group is added for "Foo" and another one for "Foo|Bar". In the returned map, the key is the * property, and the value is the group. If the property doesn't have a group (category), then it will not have an entry in the map. Note that * the property must opt-in to grouping by specifying the "EnableCategories" metadata tag. */ UE_API void GetPropertyGroups(const TArray>& InProperties, IDetailChildrenBuilder& InChildBuilder, TMap, IDetailGroup*>& OutPropertyToGroup) const; /** Cached instance types, used to invalidate the layout when types change. */ TArray> CachedInstanceTypes; /** Handle to the struct property being edited */ TSharedPtr StructProperty; /** Delegate that can be used to refresh the child rows of the current struct (eg, when changing struct type) */ FSimpleDelegate OnRegenerateChildren; /** True if we're allowed to handle a StructValuePostChange */ bool bCanHandleStructValuePostChange = false; FDelegateHandle UserDefinedStructReinstancedHandle; protected: UE_API void OnStructLayoutChanges(); }; class FInstancedStructProvider : public IStructureDataProvider { public: FInstancedStructProvider() = default; explicit FInstancedStructProvider(const TSharedPtr& InStructProperty) : StructProperty(InStructProperty) { } virtual ~FInstancedStructProvider() override { } UE_API void Reset(); UE_API virtual bool IsValid() const override; UE_API virtual const UStruct* GetBaseStructure() const override; UE_API virtual void GetInstances(TArray>& OutInstances, const UStruct* ExpectedBaseStructure) const override; UE_API virtual bool IsPropertyIndirection() const override; UE_API virtual uint8* GetValueBaseAddress(uint8* ParentValueAddress, const UStruct* ExpectedBaseStructure) const override; protected: UE_API void EnumerateInstances(TFunctionRef InFunc) const; TSharedPtr StructProperty; }; #undef UE_API