// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Modules/ModuleInterface.h" #include "Misc/Attribute.h" #include "Features/IModularFeature.h" #include "Input/Reply.h" #include "Styling/SlateColor.h" #include "UObject/Field.h" #include "UObject/UnrealType.h" class UBlueprint; class IPropertyHandle; class UEdGraph; class FExtender; class SWidget; struct FSlateBrush; struct FButtonStyle; struct FEdGraphPinType; class IPropertyAccessLibraryCompiler; struct FPropertyAccessLibrary; /** An element in a binding chain */ struct FBindingChainElement { FBindingChainElement(FProperty* InProperty, int32 InArrayIndex = INDEX_NONE) : Field(InProperty) , ArrayIndex(InArrayIndex) {} FBindingChainElement(UFunction* InFunction) : Field(InFunction) , ArrayIndex(INDEX_NONE) {} /** Field that this this chain element refers to */ FFieldVariant Field; /** Optional array index if this element refers to an array */ int32 ArrayIndex = INDEX_NONE; }; /** * Info about a redirector binding. * Redirector bindings allow */ struct FRedirectorBindingInfo { FRedirectorBindingInfo(FName InName, const FText& InDescription, UStruct* InStruct) : Name(InName) , Description(InDescription) , Struct(InStruct) {} /** The name of the binding */ FName Name = NAME_None; /** Description of the binding, used as tooltip text */ FText Description; /** The struct that the binding will output */ UStruct* Struct = nullptr; }; /** * Binding context struct allow to describe information for a struct to bind to using the binding widget. An array of structs is passed to the widget to describe the context in which the binding exists. * When the widget selectes a property from binding context struct array, the first FBindingChainElement's index correlates to the array passed to the widget. */ struct FBindingContextStruct { FBindingContextStruct() = default; FBindingContextStruct(UStruct* InStruct, const FSlateBrush* InIcon = nullptr, const FText& InDisplayText = FText::GetEmpty(), const FText& InTooltipText = FText::GetEmpty(), const FText& InSection = FText::GetEmpty()) : Struct(InStruct) , Icon(InIcon) , DisplayText(InDisplayText) , TooltipText(InTooltipText) , Section(InSection) {} /** The struct to bind to. */ UStruct* Struct = nullptr; /** Icon to display in the popup menu. */ const FSlateBrush* Icon = nullptr; /** Color of the icon to display in the popup menu. */ TOptional Color; /** Text to display for this item in the popup. If left empty, struct's display text will be used. */ FText DisplayText; /** Tooltip to show, or if empty, the tool tip will be set to the same as popup text. */ FText TooltipText; /** Name of the section to put the struct to. If left empty, no section will be created. */ FText Section; /** Category separated by | of the struct. Will display as submenus. Not part of the section to avoid converting Text to string and back. */ FString Category; }; /** Delegate used to generate a new binding function's name */ DECLARE_DELEGATE_RetVal(FString, FOnGenerateBindingName); /** Delegate used to open a binding (e.g. a function) */ DECLARE_DELEGATE_RetVal_OneParam(bool, FOnGotoBinding, FName /*InPropertyName*/); /** Delegate used to see if we can open a binding (e.g. a function) */ DECLARE_DELEGATE_RetVal_OneParam(bool, FOnCanGotoBinding, FName /*InPropertyName*/); /** Delegate used to check whether a property is considered for binding. Returning false will discard the property and all child properties. */ DECLARE_DELEGATE_RetVal_TwoParams(bool, FOnCanAcceptPropertyOrChildrenWithBindingChain, FProperty* /*InProperty*/, TConstArrayView /*InBindingChain*/); // UE_DEPRECATED(5.4, "Please use OnCanAcceptPropertyOrChildrenWithBindingChain instead.") DECLARE_DELEGATE_RetVal_OneParam(bool, FOnCanAcceptPropertyOrChildren, FProperty* /*InProperty*/); /** Delegate used to check whether a property can be bound to the property in question */ DECLARE_DELEGATE_RetVal_TwoParams(bool, FOnCanBindPropertyWithBindingChain, FProperty* /*InProperty*/, TConstArrayView /*InBindingChain*/); // UE_DEPRECATED(5.4, "Please use OnCanBindPropertyWithBindingChain instead.") DECLARE_DELEGATE_RetVal_OneParam(bool, FOnCanBindProperty, FProperty* /*InProperty*/); /** Delegate used to check whether a function can be bound to the property in question */ DECLARE_DELEGATE_RetVal_OneParam(bool, FOnCanBindFunction, UFunction* /*InFunction*/); /** Delegate called to see if a class can be bound to */ DECLARE_DELEGATE_RetVal_OneParam(bool, FOnCanBindToClass, UClass* /*InClass*/); // UE_DEPRECATED(5.5, "Please use OnCanBindToContextStructWithIndex instead.") DECLARE_DELEGATE_RetVal_OneParam(bool, FOnCanBindToContextStruct, UStruct* /*InStruct*/); /** Delegate called to see if a class can be bound to */ DECLARE_DELEGATE_RetVal_TwoParams(bool, FOnCanBindToContextStructWithIndex, UStruct* /*InStruct*/, int32 /*InStructIndex*/); /** Delegate called to see if a subobject can be bound to */ DECLARE_DELEGATE_RetVal_OneParam(bool, FOnCanBindToSubObjectClass, UClass* /*InSubObjectClass*/); /** Delegate called to add a binding */ DECLARE_DELEGATE_TwoParams(FOnAddBinding, FName /*InPropertyName*/, const TArray& /*InBindingChain*/); /** Delegate called to remove a binding */ DECLARE_DELEGATE_OneParam(FOnRemoveBinding, FName /*InPropertyName*/); /** Delegate called to see if we can remove a binding (ie. if it exists) */ DECLARE_DELEGATE_RetVal_OneParam(bool, FOnCanRemoveBinding, FName /*InPropertyName*/); /** Delegate called once a new function binding has been created */ DECLARE_DELEGATE_TwoParams(FOnNewFunctionBindingCreated, UEdGraph* /*InFunctionGraph*/, UFunction* /*InFunction*/); /** Delegate called to resolve true type of the instance */ DECLARE_DELEGATE_RetVal_OneParam(UStruct*, FOnResolveIndirection, const TArray& /*InBindingChain*/); /** Delegate called once a drag-drop event is dropped on the binding widget */ DECLARE_DELEGATE_RetVal_TwoParams(FReply, FOnDrop, const FGeometry&, const FDragDropEvent&); /** Delegate called to see if the property has any bindings */ DECLARE_DELEGATE_RetVal(bool, FOnHasAnyBindings); /** Setup arguments structure for a property binding widget */ struct FPropertyBindingWidgetArgs { // Macro needed to avoid deprecation errors when the struct is copied or created in the default methods. PRAGMA_DISABLE_DEPRECATION_WARNINGS FPropertyBindingWidgetArgs() = default; FPropertyBindingWidgetArgs(const FPropertyBindingWidgetArgs&) = default; FPropertyBindingWidgetArgs(FPropertyBindingWidgetArgs&&) = default; FPropertyBindingWidgetArgs& operator=(const FPropertyBindingWidgetArgs&) = default; FPropertyBindingWidgetArgs& operator=(FPropertyBindingWidgetArgs&&) = default; PRAGMA_ENABLE_DEPRECATION_WARNINGS /** An optional bindable property */ FProperty* Property = nullptr; /** An optional signature to use to match binding functions */ UFunction* BindableSignature = nullptr; /** Delegate used to generate a new binding function's name */ FOnGenerateBindingName OnGenerateBindingName; /** Delegate used to open a bound generated function */ FOnGotoBinding OnGotoBinding; /** Delegate used to see if we can open a binding (e.g. a function) */ FOnCanGotoBinding OnCanGotoBinding; /** Delegate used to check whether a property is considered for binding. Returning false will discard the property and all child properties. */ FOnCanAcceptPropertyOrChildrenWithBindingChain OnCanAcceptPropertyOrChildrenWithBindingChain; UE_DEPRECATED(5.4, "Please use OnCanAcceptPropertyOrChildrenWithBindingChain instead.") FOnCanAcceptPropertyOrChildren OnCanAcceptPropertyOrChildren; /** Delegate used to check whether a property can be bound to the property in question */ FOnCanBindPropertyWithBindingChain OnCanBindPropertyWithBindingChain; UE_DEPRECATED(5.4, "Please use OnCanBindPropertyWithBindingChain instead.") FOnCanBindProperty OnCanBindProperty; /** Delegate used to check whether a function can be bound to the property in question */ FOnCanBindFunction OnCanBindFunction; /** Delegate called to see if a class can be bound to */ FOnCanBindToClass OnCanBindToClass; UE_DEPRECATED(5.5, "Please use OnCanBindToContextStructWithIndex instead.") FOnCanBindToContextStruct OnCanBindToContextStruct; /** Delegate called to see if a context struct can be directly bound to */ FOnCanBindToContextStructWithIndex OnCanBindToContextStructWithIndex; /** Delegate called to see if a subobject can be bound to */ FOnCanBindToSubObjectClass OnCanBindToSubObjectClass; /** Delegate called to add a binding */ FOnAddBinding OnAddBinding; /** Delegate called to remove a binding */ FOnRemoveBinding OnRemoveBinding; /** Delegate called to see if we can remove remove a binding (ie. if it exists) */ FOnCanRemoveBinding OnCanRemoveBinding; /** Delegate called to see if the property has any bindings */ FOnHasAnyBindings OnHasAnyBindings; /** Delegate called once a new function binding has been created */ FOnNewFunctionBindingCreated OnNewFunctionBindingCreated; /** Delegate called to resolve true type of the instance */ FOnResolveIndirection OnResolveIndirection; /** Delegate called when a property is dropped on the property binding widget */ FOnDrop OnDrop; /** The current binding's text label */ TAttribute CurrentBindingText; /** The current binding's text label color */ TAttribute CurrentBindingTextColor; /** The current binding's tooltip text label */ TAttribute CurrentBindingToolTipText; /** The current binding's image */ TAttribute CurrentBindingImage; /** The current binding's color */ TAttribute CurrentBindingColor; /** Menu extender */ TSharedPtr MenuExtender; /** Optional style override for bind button */ const FButtonStyle* BindButtonStyle = nullptr; /** The maximum level of depth to generate */ uint8 MaxDepth = 10; /** Whether to generate pure bindings */ bool bGeneratePureBindings = true; /** Whether to allow function bindings (to "this" class of the blueprint in question) */ bool bAllowFunctionBindings = true; /** Whether to allow function library bindings in addition to the passed-in blueprint's class */ bool bAllowFunctionLibraryBindings = false; /** Whether to allow property bindings */ bool bAllowPropertyBindings = true; /** Whether to allow array element bindings */ bool bAllowArrayElementBindings = false; /** Whether to allow struct member bindings */ bool bAllowStructMemberBindings = true; /** Whether to allow new bindings to be made from within the widget's UI */ bool bAllowNewBindings = true; /** Whether to allow UObject functions as non-leaf nodes */ bool bAllowUObjectFunctions = false; /** Whether to allow only functions marked thread safe */ bool bAllowOnlyThreadSafeFunctions = false; /** Whether to allow UScriptStruct functions as non-leaf nodes */ bool bAllowStructFunctions = false; /** Whether to format the SPropertyBinding widget to use a link icon (versus the standard combo button) */ bool bUseLinkIconStyle = false; }; /** Enum describing the result of ResolvePropertyAccess */ enum class EPropertyAccessResolveResult { /** Resolution of the path failed */ Failed, /** DEPRECATED - Resolution of the path succeeded and the property is internal to the initial context */ SucceededInternal, /** DEPRECATED - Resolution of the path failed and the property is external to the initial context (i.e. uses an object/redirector indirection) */ SucceededExternal, /** Resolution of the path succeeded */ Succeeded, }; /** * Result of a property access resolve. Provides information about what the compiler was able to determine about the * property access. */ struct FPropertyAccessResolveResult { // The success fail of the resolve EPropertyAccessResolveResult Result = EPropertyAccessResolveResult::Failed; // Whether the resolve was determined to be thread safe bool bIsThreadSafe = false; }; /** Enum describing property compatibility */ enum class EPropertyAccessCompatibility { // Properties are incompatible Incompatible, // Properties are directly compatible Compatible, // Properties can be copied with a simple type promotion Promotable, }; // Context struct describing relevant characteristics of a property copy. // Used by client code to determine batch ID when called back via FOnPropertyAccessDetermineBatchId struct FPropertyAccessCopyContext { // The object (usually a K2 node) in which the context takes place. Used for error reporting. UObject* Object; // User-define context name, passed via IPropertyAccessLibraryCompiler::AddCopy FName ContextId; // Source path as text, for error reporting FText SourcePathAsText; // Dest path as text, for error reporting FText DestPathAsText; // Whether the source path is thread safe bool bSourceThreadSafe; // Whether the dest path is thread safe bool bDestThreadSafe; }; // Delegate used to determine batch ID (index) for a particular copy context DECLARE_DELEGATE_RetVal_OneParam(int32, FOnPropertyAccessDetermineBatchId, const FPropertyAccessCopyContext& /*InContext*/); // Context used to create a property access library compiler struct FPropertyAccessLibraryCompilerArgs { FPropertyAccessLibraryCompilerArgs(FPropertyAccessLibrary& InLibrary, const UClass* InClassContext) : Library(InLibrary) , ClassContext(InClassContext) {} // The library that will be built FPropertyAccessLibrary& Library; // The class that provides a root context for the library to be built in const UClass* ClassContext; // Delegate used to determine batch ID (index) for a particular copy context. If this is not set, then all copies // will be batched together in batch 0 FOnPropertyAccessDetermineBatchId OnDetermineBatchId; }; /** Editor support for property access system */ class IPropertyAccessEditor : public IModularFeature { public: virtual ~IPropertyAccessEditor() {} /** * Make a property binding widget. * @param InBlueprint The blueprint that the binding will exist within * @param InArgs Optional arguments for the widget * @return a new binding widget */ virtual TSharedRef MakePropertyBindingWidget(UBlueprint* InBlueprint, const FPropertyBindingWidgetArgs& InArgs = FPropertyBindingWidgetArgs()) const = 0; /** * Make a property binding widget. * @param InBindingContextStructs An array of structs the binding will exist within * @param InArgs Optional arguments for the widget * @return a new binding widget */ virtual TSharedRef MakePropertyBindingWidget(const TArray& InBindingContextStructs, const FPropertyBindingWidgetArgs& InArgs = FPropertyBindingWidgetArgs()) const = 0; /** Resolve a property access, returning the leaf property and array index if any. @return result structure for more information about the result */ virtual FPropertyAccessResolveResult ResolvePropertyAccess(const UStruct* InStruct, TArrayView InPath, FProperty*& OutProperty, int32& OutArrayIndex) const = 0; /** Args used to resolve property access segments. The various functions will be called per-segment when resolved. */ struct FResolvePropertyAccessArgs { /** Function called when a property is resolved. Array index is valid for static array properties. For dynamic array properties, use ArrayFunction. */ TFunction PropertyFunction; /** Function called when a dynamic array is resolved. */ TFunction ArrayFunction; /** Function called when a function is resolved. */ TFunction FunctionFunction; /** * Whether to use the most up to date classes when traversing the path. * This can be useful for situations where we are resolving against potentially out of date * classes, but the resulting path will not be valid to use or persist due to functions and properties * being on skeleton classes */ bool bUseMostUpToDateClasses = false; }; /** * Resolve a property path to a structure, calling back for each segment in path segment order if resolution succeed * @return true if resolution succeeded */ virtual FPropertyAccessResolveResult ResolvePropertyAccess(const UStruct* InStruct, TArrayView InPath, const FResolvePropertyAccessArgs& InArgs) const = 0; UE_DEPRECATED(5.0, "Please use ResolvePropertyAccess") virtual EPropertyAccessResolveResult ResolveLeafProperty(const UStruct* InStruct, TArrayView InPath, FProperty*& OutProperty, int32& OutArrayIndex) const { const FPropertyAccessResolveResult Result = ResolvePropertyAccess(InStruct, InPath, OutProperty, OutArrayIndex); return Result.Result; } // Get the compatibility of the two supplied properties. Ordering matters for promotion (A->B). virtual EPropertyAccessCompatibility GetPropertyCompatibility(const FProperty* InPropertyA, const FProperty* InPropertyB) const = 0; // Get the compatibility of the two supplied pin types. Ordering matters for promotion (A->B). virtual EPropertyAccessCompatibility GetPinTypeCompatibility(const FEdGraphPinType& InPinTypeA, const FEdGraphPinType& InPinTypeB) const = 0; // Makes a string path from a binding chain virtual void MakeStringPath(const TArray& InBindingChain, TArray& OutStringPath) const = 0; // Make a property access library compiler, used for building a FPropertyAccessLibrary virtual TUniquePtr MakePropertyAccessCompiler(const FPropertyAccessLibraryCompilerArgs& InArgs) const = 0; // Make a text representation of a property path // @param InPath The path to use to generate the text path // @param InStruct Optional struct to resolve against - if this is supplied then the text path can use 'correct' display names virtual FText MakeTextPath(const TArray& InPath, const UStruct* InStruct = nullptr) const = 0; };