// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "AssetRegistry/AssetData.h" #include "CoreMinimal.h" #include "UObject/Object.h" #include "Misc/Attribute.h" #include "Layout/Margin.h" #include "Fonts/SlateFontInfo.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SWidget.h" #include "Widgets/SCompoundWidget.h" #include "Widgets/SBoxPanel.h" #include "Framework/SlateDelegates.h" #include "Materials/MaterialInterface.h" #include "PropertyHandle.h" #include "IDetailCustomNodeBuilder.h" #include "DetailWidgetRow.h" #include "SResetToDefaultMenu.h" #include "ActorPickerMode.h" #include "SceneDepthPickerMode.h" #include "IDetailPropertyRow.h" #include "ClassViewerModule.h" class AActor; class FAssetThumbnailPool; class FPropertyEditor; class IClassViewerFilter; class IDetailCategoryBuilder; class IDetailChildrenBuilder; class IDetailGroup; class IDetailLayoutBuilder; class IPropertyHandle; class SPropertyEditorAsset; class SPropertyEditorClass; class SPropertyEditorStruct; class SToolTip; class UActorComponent; class UFactory; class FPropertyNode; struct FAssetData; struct FSceneOutlinerFilters; DECLARE_DELEGATE_OneParam(FOnAssetSelected, const FAssetData& /*AssetData*/); DECLARE_DELEGATE_RetVal_OneParam(bool, FOnShouldSetAsset, const FAssetData& /*AssetData*/); DECLARE_DELEGATE_RetVal_OneParam(bool, FOnShouldFilterAsset, const FAssetData& /*AssetData*/); DECLARE_DELEGATE_OneParam(FOnComponentSelected, const UActorComponent* /*ActorComponent*/); DECLARE_DELEGATE_RetVal_OneParam(bool, FOnShouldFilterComponent, const UActorComponent* /*ActorComponent*/); DECLARE_DELEGATE_OneParam( FOnGetActorFilters, TSharedPtr& ); DECLARE_DELEGATE_ThreeParams(FOnGetPropertyComboBoxStrings, TArray< TSharedPtr >&, TArray>&, TArray&); DECLARE_DELEGATE_RetVal(FString, FOnGetPropertyComboBoxValue); DECLARE_DELEGATE_OneParam(FOnPropertyComboBoxValueSelected, const FString&); DECLARE_DELEGATE_ThreeParams(FOnInstancedPropertyIteration, IDetailCategoryBuilder&, IDetailGroup*, TSharedRef&); DECLARE_DELEGATE_RetVal(bool, FOnIsEnabled); DECLARE_DELEGATE_TwoParams(FOnSetOptional, FProperty*, const UClass*); /** Collects advanced arguments for MakePropertyComboBox */ struct FPropertyComboBoxArgs { /** If set, the combo box will bind to a specific property. If this is null, the following 3 delegates must be set */ TSharedPtr PropertyHandle; /** Delegate that is called to generate the list of possible strings inside the combo box list. If not set it will generate using the property handle */ FOnGetPropertyComboBoxStrings OnGetStrings; /** Delegate that is called to get the current string value to display as the combo box label. If not set it will generate using the property handle */ FOnGetPropertyComboBoxValue OnGetValue; /** Delegate called when a string is selected. If not set it will modify what is bound to the property handle */ FOnPropertyComboBoxValueSelected OnValueSelected; /** If number of items in combo box is >= this, it will show a search box to allow filtering. -1 means to never show it */ int32 ShowSearchForItemCount = 20; /** Font to use for text display. If not set it will use the default property editor font */ FSlateFontInfo Font; /** Default constructor, the caller will need to fill in values manually */ FPropertyComboBoxArgs() {} /** Constructor using original function arguments */ FPropertyComboBoxArgs(const TSharedPtr& InPropertyHandle, FOnGetPropertyComboBoxStrings InOnGetStrings = FOnGetPropertyComboBoxStrings(), FOnGetPropertyComboBoxValue InOnGetValue = FOnGetPropertyComboBoxValue(), FOnPropertyComboBoxValueSelected InOnValueSelected = FOnPropertyComboBoxValueSelected()) : PropertyHandle(InPropertyHandle) , OnGetStrings(InOnGetStrings) , OnGetValue(InOnGetValue) , OnValueSelected(InOnValueSelected) {} }; struct FPropertyFunctionCallArgs { TWeakObjectPtr Function; TOptional LabelOverride; TOptional ToolTipTextOverride; using FOnExecute = TDelegate)>; FOnExecute OnExecute; using FOnCanExecute = TDelegate)>; FOnCanExecute OnCanExecute; FTextBuilder* SearchText = nullptr; FPropertyFunctionCallArgs( UFunction* InFunction, const FOnExecute& InOnExecute, const FOnCanExecute& InOnCanExecute = {}, const TOptional& InLabelOverride = {}, const TOptional& InToolTipTextOverride = {}, FTextBuilder* InSearchText = nullptr) : Function(InFunction) , LabelOverride(InLabelOverride) , ToolTipTextOverride(InToolTipTextOverride) , OnExecute(InOnExecute) , OnCanExecute(InOnCanExecute) , SearchText(InSearchText) { } }; /** The callbacks, if specified, are used when invoking function calls. */ struct FPropertyFunctionCallDelegates { using FOnGetExecutionContext = TDelegate>(TWeakObjectPtr)>; FOnGetExecutionContext OnGetExecutionContext; using FOnExecute = TDelegate)>; FOnExecute OnExecute; using FOnCanExecute = TDelegate)>; FOnCanExecute OnCanExecute; FPropertyFunctionCallDelegates( const FOnExecute& InOnExecute, const FOnCanExecute& InOnCanExecute = {}) : OnExecute(InOnExecute) , OnCanExecute(InOnCanExecute) { } FPropertyFunctionCallDelegates(const FOnGetExecutionContext& InOnGetExecutionContext) : OnGetExecutionContext(InOnGetExecutionContext) { } }; namespace PropertyCustomizationHelpers { PROPERTYEDITOR_API TSharedRef MakeCustomButton(const FSlateBrush* IconBrush, FSimpleDelegate OnClicked, TAttribute ToolTipText = FText(), TAttribute IsEnabled = true, TAttribute IsVisible = EVisibility::Visible); PROPERTYEDITOR_API TSharedRef MakeResetButton(FSimpleDelegate OnResetClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true); PROPERTYEDITOR_API TSharedRef MakeAddButton( FSimpleDelegate OnAddClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true ); PROPERTYEDITOR_API TSharedRef MakeRemoveButton( FSimpleDelegate OnRemoveClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true ); PROPERTYEDITOR_API TSharedRef MakeEditButton( FSimpleDelegate OnEditClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true ); PROPERTYEDITOR_API TSharedRef MakeEmptyButton( FSimpleDelegate OnEmptyClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true ); PROPERTYEDITOR_API TSharedRef MakeInsertDeleteDuplicateButton( FExecuteAction OnInsertClicked, FExecuteAction OnDeleteClicked, FExecuteAction OnDuplicateClicked ); PROPERTYEDITOR_API TSharedRef MakeDeleteButton( FSimpleDelegate OnDeleteClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true ); PROPERTYEDITOR_API TSharedRef MakeClearButton( FSimpleDelegate OnClearClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true ); PROPERTYEDITOR_API TSharedRef MakeVisibilityButton(FOnClicked OnVisibilityClicked, TAttribute OptionalToolTipText = FText(), TAttribute VisibilityDelegate = true); PROPERTYEDITOR_API TSharedRef MakeNewBlueprintButton( FSimpleDelegate OnNewBlueprintClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true ); PROPERTYEDITOR_API TSharedRef MakeUseSelectedButton( FSimpleDelegate OnUseSelectedClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true, const bool IsActor = false ); PROPERTYEDITOR_API TSharedRef MakeBrowseButton( FSimpleDelegate OnFindClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true, const bool IsActor = false, const TAttribute& InIconOverride = TAttribute()); PROPERTYEDITOR_API TSharedRef MakeAssetPickerAnchorButton( FOnGetAllowedClasses OnGetAllowedClasses, FOnAssetSelected OnAssetSelectedFromPicker, const TSharedPtr& PropertyHandle = TSharedPtr()); PROPERTYEDITOR_API TSharedRef MakeAssetPickerWithMenu( const FAssetData& InitialObject, const bool AllowClear, const TArray& AllowedClasses, const TArray& NewAssetFactories, FOnShouldFilterAsset OnShouldFilterAsset, FOnAssetSelected OnSet, FSimpleDelegate OnClose, const TSharedPtr& PropertyHandle = TSharedPtr(), const TArray& OwnerAssetArray = TArray()); PROPERTYEDITOR_API TSharedRef MakeAssetPickerWithMenu( const FAssetData& InitialObject, const bool AllowClear, const TArray& AllowedClasses, const TArray& DisallowedClasses, const TArray& NewAssetFactories, FOnShouldFilterAsset OnShouldFilterAsset, FOnAssetSelected OnSet, FSimpleDelegate OnClose, const TSharedPtr& PropertyHandle = TSharedPtr(), const TArray& OwnerAssetArray = TArray()); PROPERTYEDITOR_API TSharedRef MakeAssetPickerWithMenu( const FAssetData& InitialObject, const bool AllowClear, const bool AllowCopyPaste, const TArray& AllowedClasses, const TArray& NewAssetFactories, FOnShouldFilterAsset OnShouldFilterAsset, FOnAssetSelected OnSet, FSimpleDelegate OnClose, const TSharedPtr& PropertyHandle = TSharedPtr(), const TArray& OwnerAssetArray = TArray()); PROPERTYEDITOR_API TSharedRef MakeAssetPickerWithMenu( const FAssetData& InitialObject, const bool AllowClear, const bool AllowCopyPaste, const TArray& AllowedClasses, const TArray& DisallowedClasses, const TArray& NewAssetFactories, FOnShouldFilterAsset OnShouldFilterAsset, FOnAssetSelected OnSet, FSimpleDelegate OnClose, const TSharedPtr& PropertyHandle = TSharedPtr(), const TArray& OwnerAssetArray = TArray()); PROPERTYEDITOR_API TSharedRef MakeActorPickerAnchorButton( FOnGetActorFilters OnGetActorFilters, FOnActorSelected OnActorSelectedFromPicker ); PROPERTYEDITOR_API TSharedRef MakeActorPickerWithMenu( AActor* const InitialActor, const bool AllowClear, FOnShouldFilterActor ActorFilter, FOnActorSelected OnSet, FSimpleDelegate OnClose, FSimpleDelegate OnUseSelected ); PROPERTYEDITOR_API TSharedRef MakeActorPickerWithMenu(AActor* const InitialActor, const bool AllowClear, const bool AllowPickingLevelInstanceContent, FOnShouldFilterActor ActorFilter, FOnActorSelected OnSet, FSimpleDelegate OnClose, FSimpleDelegate OnUseSelected, bool bDisplayUseSelected=true, bool bShowTransient=false); PROPERTYEDITOR_API TSharedRef MakeComponentPickerWithMenu( UActorComponent* const InitialComponent, const bool AllowClear, FOnShouldFilterActor ActorFilter, FOnShouldFilterComponent ComponentFilter, FOnComponentSelected OnSet, FSimpleDelegate OnClose ); PROPERTYEDITOR_API TSharedRef MakeInteractiveActorPicker(FOnGetAllowedClasses OnGetAllowedClasses, FOnShouldFilterActor OnShouldFilterActor, FOnActorSelected OnActorSelectedFromPicker); PROPERTYEDITOR_API TSharedRef MakeSceneDepthPicker(FOnSceneDepthLocationSelected OnSceneDepthLocationSelected); PROPERTYEDITOR_API TSharedRef MakeEditConfigHierarchyButton(FSimpleDelegate OnEditConfigClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true); PROPERTYEDITOR_API TSharedRef MakeDocumentationButton(const TSharedRef& InPropertyEditor); PROPERTYEDITOR_API TSharedRef MakeSaveButton(FSimpleDelegate OnSaveClicked, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true); PROPERTYEDITOR_API TSharedRef MakeSetOptionalButton(FOnSetOptional OnSetOptional, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true); PROPERTYEDITOR_API TSharedRef MakePickOptionalButton(FOnSetOptional OnSetOptional, FSimpleDelegate OnClearOptional, const TSharedRef& PropertyNode, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true); PROPERTYEDITOR_API TSharedRef MakeClearOptionalButton(FSimpleDelegate OnClearOptional, TAttribute OptionalToolTipText = FText(), TAttribute IsEnabled = true); PROPERTYEDITOR_API TSharedRef MakeFunctionCallButton(const FPropertyFunctionCallArgs& InArgs); PROPERTYEDITOR_API void AddFunctionCallWidgets(IDetailGroup& RootGroup, const TArrayView& InCallInEditorFunctions, const FPropertyFunctionCallDelegates& InArgs); PROPERTYEDITOR_API void AddFunctionCallWidgets(IDetailLayoutBuilder& DetailBuilder, const TArrayView& InCallInEditorFunctions, const FPropertyFunctionCallDelegates& InArgs); PROPERTYEDITOR_API void AddCallInEditorFunctionCallWidgetsForClass(IDetailGroup& RootGroup, const UClass* Class, const FPropertyFunctionCallDelegates& InArgs); PROPERTYEDITOR_API void AddCallInEditorFunctionCallWidgetsForClass(IDetailLayoutBuilder& DetailBuilder, const UClass* Class, const FPropertyFunctionCallDelegates& InArgs); /** @return the FBoolProperty edit condition property if one exists. */ PROPERTYEDITOR_API FBoolProperty* GetEditConditionProperty(const FProperty* InProperty, bool& bNegate); /** Returns a list of factories which can be used to create new assets, based on the supplied class */ PROPERTYEDITOR_API TArray GetNewAssetFactoriesForClasses(const TArray& Classes); /** Returns a list of factories which can be used to create new assets, based on the supplied classes and respecting the disallowed set */ PROPERTYEDITOR_API TArray GetNewAssetFactoriesForClasses(const TArray& Classes, const TArray& DisallowedClasses); /** * Build a combo button that you bind to a Name/String/Enum property or display using general delegates, using an arguments structure * * @param InArgs Options used to create combo box */ PROPERTYEDITOR_API TSharedRef MakePropertyComboBox(const FPropertyComboBoxArgs& InArgs); /** * Build a combo button that you bind to a Name/String/Enum property or display using general delegates * * @param InPropertyHandle If set, will bind to a specific property. If this is null, all 3 delegates must be set * @param OnGetStrings Delegate that will generate the list of possible strings. If not set will generate using property handle * @param OnGetValue Delegate that is called to get the current string value to display. If not set will generate using property handle * @param OnValueSelected Delegate called when a string is selected. If not set will set the property handle */ PROPERTYEDITOR_API TSharedRef MakePropertyComboBox(const TSharedPtr& InPropertyHandle, FOnGetPropertyComboBoxStrings OnGetStrings = FOnGetPropertyComboBoxStrings(), FOnGetPropertyComboBoxValue OnGetValue = FOnGetPropertyComboBoxValue(), FOnPropertyComboBoxValueSelected OnValueSelected = FOnPropertyComboBoxValueSelected()); /** * Loops through all of an instanced object property's child properties and call AddRowDelegate on properties that needs to be added to the UI to let us customize it. * * @param ExistingGroup A map used internally to determine the existing group categories. Should be empty. * @param BaseCategory The category that will be used as the root of the instanced object properties. * @param BaseProperty The instanced class property. * @param AddRowDelegate The delegate that will be called on each child property. */ PROPERTYEDITOR_API void MakeInstancedPropertyCustomUI(TMap& ExistingGroup, IDetailCategoryBuilder& BaseCategory, TSharedRef& BaseProperty, FOnInstancedPropertyIteration AddRowDelegate); /** * Parse and load the given metadata string into a list of allowed classes. * The metadata string is likely from something like AllowedClasses or DisallowedClasses. */ PROPERTYEDITOR_API TArray GetClassesFromMetadataString(const FString& MetadataString); /** * Parse and load the given metadata string into a list of allowed structs. * The metadata string is likely from something like AllowedClasses or DisallowedClasses. */ PROPERTYEDITOR_API TArray GetStructsFromMetadataString(const FString& MetadataString); /** * */ PROPERTYEDITOR_API void GetCallInEditorFunctionsForClass(const UClass* InClass, TArray& OutCallInEditorFunctions, EFieldIterationFlags InIterationFlags = EFieldIterationFlags::IncludeSuper); /** * @param InFunctionFilter A filter to select candidate UFunctions */ PROPERTYEDITOR_API void GetCallInEditorFunctionsForClass(const UClass* InClass, const TFunctionRef& InFunctionFilter, TArray& OutCallInEditorFunctions, EFieldIterationFlags InIterationFlags = EFieldIterationFlags::IncludeSuper); /* * Makes a class picker widget for the given instanced editinline UObject property handle. Shares code with SPropertyEditorInline, but doesn't create a combo button, just the class picker. */ PROPERTYEDITOR_API TSharedRef MakeEditInlineObjectClassPicker(TSharedRef PropertyHandle, FOnClassPicked OnClassPicked, TSharedPtr AdditionalClassFilter=nullptr); /* * Creates a new instance of the given object class inside the given property handle, mimicking what the SPropertyEditorEditInline widget does on class selection */ PROPERTYEDITOR_API void CreateNewInstanceOfEditInlineObjectClass(TSharedRef PropertyHandle, UClass* Class, EPropertyValueSetFlags::Type Flags = EPropertyValueSetFlags::DefaultFlags); } /** Delegate used to get a generic object */ DECLARE_DELEGATE_RetVal( const UObject*, FOnGetObject ); /** Delegate used to set a generic object */ DECLARE_DELEGATE_OneParam( FOnSetObject, const FAssetData& ); /** * Simulates an object property field * Can be used when a property should act like a FObjectProperty but it isn't one */ class SObjectPropertyEntryBox : public SCompoundWidget { public: SLATE_BEGIN_ARGS( SObjectPropertyEntryBox ) : _AllowedClass( UObject::StaticClass() ) , _AllowClear( true ) , _DisplayUseSelected( true ) , _DisplayBrowse( true ) , _EnableContentPicker(true) , _DisplayCompactSize(false) , _DisplayThumbnail(true) {} /** The path to the object */ SLATE_ATTRIBUTE( FString, ObjectPath ) /** Optional property handle that can be used instead of the object path */ SLATE_ARGUMENT( TSharedPtr, PropertyHandle ) /** Optional, array of the objects path, in case the property handle is not valid we will use this one to pass additional object to the picker config*/ SLATE_ARGUMENT(TArray, OwnerAssetDataArray) /** Thumbnail pool */ SLATE_ARGUMENT( TSharedPtr, ThumbnailPool ) /** Class that is allowed in the asset picker */ SLATE_ARGUMENT( const UClass*, AllowedClass ) /** Optional list of factories which may be used to create new assets */ SLATE_ARGUMENT( TOptional>, NewAssetFactories ) /** Called to check if an asset should be set */ SLATE_EVENT(FOnShouldSetAsset, OnShouldSetAsset) /** Called when the object value changes */ SLATE_EVENT(FOnSetObject, OnObjectChanged) /** Called to check if an asset is valid to use */ SLATE_EVENT(FOnShouldFilterAsset, OnShouldFilterAsset) /** Called to check if the asset should be enabled. */ SLATE_EVENT(FOnIsEnabled, OnIsEnabled) /** Whether the asset can be 'None' */ SLATE_ARGUMENT(bool, AllowClear) /** Whether the asset can be created from the asset picker directly */ SLATE_ARGUMENT(bool, AllowCreate) /** Whether to show the 'Use Selected' button */ SLATE_ARGUMENT(bool, DisplayUseSelected) /** Whether to show the 'Browse' button */ SLATE_ARGUMENT(bool, DisplayBrowse) /** Optional delegate called when the 'Browse' button is clicked. Used to override the default editor behavior */ SLATE_EVENT(FSimpleDelegate, OnBrowseOverride) /** Whether to enable the content Picker */ SLATE_ARGUMENT(bool, EnableContentPicker) /** Whether or not to display a smaller, compact size for the asset thumbnail */ SLATE_ARGUMENT(bool, DisplayCompactSize) /** Whether or not to display the asset thumbnail */ SLATE_ARGUMENT(bool, DisplayThumbnail) /** A custom content slot for widgets */ SLATE_NAMED_SLOT(FArguments, CustomContentSlot) SLATE_ATTRIBUTE(FIntPoint, ThumbnailSizeOverride) /** Called to check if an actor is valid to use */ SLATE_EVENT(FOnShouldFilterActor, OnShouldFilterActor) /** When this is true, the drop target will only get recognized when entering while drag & dropping. */ SLATE_ATTRIBUTE(bool, bOnlyRecognizeOnDragEnter) SLATE_END_ARGS() PROPERTYEDITOR_API void Construct( const FArguments& InArgs ); PROPERTYEDITOR_API void GetDesiredWidth(float& OutMinDesiredWidth, float &OutMaxDesiredWidth); PROPERTYEDITOR_API void OpenEntryBox(); private: /** * Delegate function called when an object is changed */ void OnSetObject(const FAssetData& InObject); /** @return the object path for the object we are viewing */ FString OnGetObjectPath() const; bool IsEnabled() const; private: /** Delegate to call to determine whether the asset should be set */ FOnShouldSetAsset OnShouldSetAsset; /** Delegate to call to determine whether the actor should be allowed */ FOnShouldFilterActor OnShouldFilterActor; /** Delegate to call when the object changes */ FOnSetObject OnObjectChanged; /** Delegate to call to check if this widget should be enabled. */ FOnIsEnabled OnIsEnabled; /** Path to the object */ TAttribute ObjectPath; /** Handle to a property we modify (if any)*/ TSharedPtr PropertyHandle; /** The widget used to edit the object 'property' */ TSharedPtr PropertyEditorAsset; }; /** Delegate used to set a class */ DECLARE_DELEGATE_OneParam( FOnSetClass, const UClass* ); /** * Simulates a class property field * Can be used when a property should act like a FClassProperty but it isn't one */ class SClassPropertyEntryBox : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SClassPropertyEntryBox) : _MetaClass(UObject::StaticClass()) , _RequiredInterface(nullptr) , _AllowAbstract(false) , _IsBlueprintBaseOnly(false) , _AllowNone(true) , _HideViewOptions(false) , _ShowDisplayNames(false) , _ShowTreeView(false) {} /** The meta class that the selected class must be a child-of (required) */ SLATE_ARGUMENT(const UClass*, MetaClass) /** An interface that the selected class must implement (optional) */ SLATE_ARGUMENT(const UClass*, RequiredInterface) /** Allowed class that the selected class must be a child-of. (optional) */ SLATE_ARGUMENT(TArray, AllowedClasses) /** Classes that the selected class cannot be a child-of. (optional) */ SLATE_ARGUMENT(TArray, DisallowedClasses) /** Whether or not abstract classes are allowed (optional) */ SLATE_ARGUMENT(bool, AllowAbstract) /** Should only base blueprints be displayed? (optional) */ SLATE_ARGUMENT(bool, IsBlueprintBaseOnly) /** Should we be able to select "None" as a class? (optional) */ SLATE_ARGUMENT(bool, AllowNone) /** Show the View Options part of the class picker dialog*/ SLATE_ARGUMENT(bool, HideViewOptions) /** true to show class display names rather than their native names, false otherwise */ SLATE_ARGUMENT(bool, ShowDisplayNames) /** Show the class picker as a tree view rather than a list*/ SLATE_ARGUMENT(bool, ShowTreeView) /** Attribute used to get the currently selected class (required) */ SLATE_ATTRIBUTE(const UClass*, SelectedClass) /** Delegate used to set the currently selected class (required) */ SLATE_EVENT(FOnSetClass, OnSetClass) /** Custom class filter(s) to be applied on the derived classes of the Metaclass (may be empty)*/ SLATE_ARGUMENT(TArray>, ClassViewerFilters) SLATE_END_ARGS() PROPERTYEDITOR_API void Construct(const FArguments& InArgs); private: /** The widget used to edit the class 'property' */ TSharedPtr PropertyEditorClass; }; /** Delegate used to set a struct */ DECLARE_DELEGATE_OneParam(FOnSetStruct, const UScriptStruct*); /** * Simulates a struct type property field * Can be used when a property should act like a struct type but it isn't one */ class SStructPropertyEntryBox : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SStructPropertyEntryBox) : _MetaStruct(nullptr) , _AllowNone(true) , _HideViewOptions(false) , _ShowDisplayNames(false) , _ShowTreeView(false) {} /** The meta class that the selected struct must be a child-of (optional) */ SLATE_ARGUMENT(const UScriptStruct*, MetaStruct) /** Should we be able to select "None" as a struct? (optional) */ SLATE_ARGUMENT(bool, AllowNone) /** Show the View Options part of the struct picker dialog*/ SLATE_ARGUMENT(bool, HideViewOptions) /** true to show struct display names rather than their native names, false otherwise */ SLATE_ARGUMENT(bool, ShowDisplayNames) /** Show the struct picker as a tree view rather than a list*/ SLATE_ARGUMENT(bool, ShowTreeView) /** Attribute used to get the currently selected struct (required) */ SLATE_ATTRIBUTE(const UScriptStruct*, SelectedStruct) /** Delegate used to set the currently selected struct (required) */ SLATE_EVENT(FOnSetStruct, OnSetStruct) SLATE_END_ARGS() PROPERTYEDITOR_API void Construct(const FArguments& InArgs); private: /** The widget used to edit the struct 'property' */ TSharedPtr PropertyEditorStruct; }; /** * Represents a widget that can display a FProperty * With the ability to customize the look of the property */ class SProperty : public SCompoundWidget { public: DECLARE_DELEGATE( FOnPropertyValueChanged ); SLATE_BEGIN_ARGS( SProperty ) : _ShouldDisplayName( true ) , _DisplayResetToDefault( true ) {} /** The display name to use in the default property widget */ SLATE_ATTRIBUTE( FText, DisplayName ) /** Whether or not to display the property name */ SLATE_ARGUMENT( bool, ShouldDisplayName ) /** The widget to display for this property instead of the default */ SLATE_DEFAULT_SLOT( FArguments, CustomWidget ) /** Whether or not to display the default reset to default button. Note this value has no affect if overriding the widget */ SLATE_ARGUMENT( bool, DisplayResetToDefault ) SLATE_END_ARGS() virtual ~SProperty(){} PROPERTYEDITOR_API void Construct( const FArguments& InArgs, TSharedPtr InPropertyHandle ); /** * Resets the property to default */ PROPERTYEDITOR_API virtual void ResetToDefault(); /** * @return Whether or not the reset to default option should be visible */ PROPERTYEDITOR_API virtual bool ShouldShowResetToDefault() const; /** * @return a label suitable for displaying in a reset to default menu */ PROPERTYEDITOR_API virtual FText GetResetToDefaultLabel() const; /** * Returns whether or not this property is valid. Sometimes property widgets are created even when their FProperty is not exposed to the user. In that case the property is invalid * Properties can also become invalid if selection changes in the detail view and this value is stored somewhere. * @return Whether or not the property is valid. */ PROPERTYEDITOR_API virtual bool IsValidProperty() const; protected: /** The handle being accessed by this widget */ TSharedPtr PropertyHandle; }; DECLARE_DELEGATE_ThreeParams( FOnGenerateArrayElementWidget, TSharedRef, int32, IDetailChildrenBuilder& ); class FDetailArrayBuilder : public IDetailCustomNodeBuilder { public: FDetailArrayBuilder(TSharedRef InBaseProperty, bool InGenerateHeader = true, bool InDisplayResetToDefault = true, bool InDisplayElementNum = true) : ArrayProperty( InBaseProperty->AsArray() ) , BaseProperty( InBaseProperty ) , bGenerateHeader( InGenerateHeader) , bDisplayResetToDefault(InDisplayResetToDefault) , bDisplayElementNum(InDisplayElementNum) { check( ArrayProperty.IsValid() ); // Delegate for when the number of children in the array changes FSimpleDelegate OnNumChildrenChanged = FSimpleDelegate::CreateRaw( this, &FDetailArrayBuilder::OnNumChildrenChanged ); OnNumElementsChangedHandle = ArrayProperty->SetOnNumElementsChanged( OnNumChildrenChanged ); BaseProperty->MarkHiddenByCustomization(); } ~FDetailArrayBuilder() { ArrayProperty->UnregisterOnNumElementsChanged(OnNumElementsChangedHandle); } void SetDisplayName( const FText& InDisplayName ) { DisplayName = InDisplayName; } void OnGenerateArrayElementWidget( FOnGenerateArrayElementWidget InOnGenerateArrayElementWidget ) { OnGenerateArrayElementWidgetDelegate = InOnGenerateArrayElementWidget; } virtual bool RequiresTick() const override { return false; } virtual void Tick( float DeltaTime ) override {} virtual FName GetName() const override { return BaseProperty->GetProperty()->GetFName(); } virtual bool InitiallyCollapsed() const override { return false; } virtual void GenerateHeaderRowContent( FDetailWidgetRow& NodeRow ) override { if (bGenerateHeader) { TSharedPtr ContentHorizontalBox; SAssignNew(ContentHorizontalBox, SHorizontalBox); if (bDisplayElementNum) { ContentHorizontalBox->AddSlot() [ BaseProperty->CreatePropertyValueWidget() ]; } FUIAction CopyAction; FUIAction PasteAction; BaseProperty->CreateDefaultPropertyCopyPasteActions(CopyAction, PasteAction); NodeRow .FilterString(!DisplayName.IsEmpty() ? DisplayName : BaseProperty->GetPropertyDisplayName()) .NameContent() [ BaseProperty->CreatePropertyNameWidget(DisplayName, FText::GetEmpty()) ] .ValueContent() [ ContentHorizontalBox.ToSharedRef() ] .CopyAction(CopyAction) .PasteAction(PasteAction); if (bDisplayResetToDefault) { TSharedPtr ResetToDefaultMenu; ContentHorizontalBox->AddSlot() .AutoWidth() .Padding(FMargin(2.0f, 0.0f, 0.0f, 0.0f)) [ SAssignNew(ResetToDefaultMenu, SResetToDefaultMenu) ]; ResetToDefaultMenu->AddProperty(BaseProperty); } } } virtual void GenerateChildContent( IDetailChildrenBuilder& ChildrenBuilder ) override { uint32 NumChildren = 0; ArrayProperty->GetNumElements( NumChildren ); for( uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex ) { TSharedRef ElementHandle = ArrayProperty->GetElement( ChildIndex ); OnGenerateArrayElementWidgetDelegate.Execute( ElementHandle, ChildIndex, ChildrenBuilder ); } } virtual void RefreshChildren() { OnRebuildChildren.ExecuteIfBound(); } virtual TSharedPtr GetPropertyHandle() const { return BaseProperty; } protected: virtual void SetOnRebuildChildren( FSimpleDelegate InOnRebuildChildren ) override { OnRebuildChildren = InOnRebuildChildren; } void OnNumChildrenChanged() { OnRebuildChildren.ExecuteIfBound(); } private: FText DisplayName; FOnGenerateArrayElementWidget OnGenerateArrayElementWidgetDelegate; TSharedPtr ArrayProperty; TSharedRef BaseProperty; FSimpleDelegate OnRebuildChildren; bool bGenerateHeader; bool bDisplayResetToDefault; bool bDisplayElementNum; FDelegateHandle OnNumElementsChangedHandle; }; /** * Helper class to create a material slot name widget for material lists */ class SMaterialSlotWidget : public SCompoundWidget { SLATE_BEGIN_ARGS(SMaterialSlotWidget) : _DeleteMaterialSlotVisibility(EVisibility::Visible) {} SLATE_ATTRIBUTE(FText, MaterialName) SLATE_ATTRIBUTE(bool, IsMaterialSlotNameReadOnly) SLATE_EVENT(FOnTextChanged, OnMaterialNameChanged) SLATE_EVENT(FOnTextCommitted, OnMaterialNameCommitted) SLATE_ATTRIBUTE(bool, CanDeleteMaterialSlot) SLATE_EVENT(FSimpleDelegate, OnDeleteMaterialSlot) SLATE_ATTRIBUTE(EVisibility, DeleteMaterialSlotVisibility) SLATE_END_ARGS() PROPERTYEDITOR_API void Construct(const FArguments& InArgs, int32 SlotIndex, bool bIsMaterialUsed); }; ////////////////////////////////////////////////////////////////////////// // // SECTION LIST /** * Delegate called when we need to get new sections for the list */ DECLARE_DELEGATE_OneParam(FOnGetSections, class ISectionListBuilder&); /** * Delegate called when a user changes the Section */ DECLARE_DELEGATE_FourParams(FOnSectionChanged, int32, int32, int32, FName); DECLARE_DELEGATE_RetVal_TwoParams(TSharedRef, FOnGenerateWidgetsForSection, int32, int32); DECLARE_DELEGATE_TwoParams(FOnResetSectionToDefaultClicked, int32, int32); DECLARE_DELEGATE_RetVal_OneParam(TSharedRef, FOnGenerateLODComboBox, int32); DECLARE_DELEGATE_RetVal(bool, FOnCanCopySectionList); DECLARE_DELEGATE(FOnCopySectionList); DECLARE_DELEGATE(FOnPasteSectionList); DECLARE_DELEGATE_RetVal_TwoParams(bool, FOnCanCopySectionItem, int32, int32); DECLARE_DELEGATE_TwoParams(FOnCopySectionItem, int32, int32); DECLARE_DELEGATE_TwoParams(FOnPasteSectionItem, int32, int32); DECLARE_DELEGATE_ThreeParams(FOnEnableSectionItem, int32, int32, bool); struct FSectionListDelegates { FSectionListDelegates() : OnGetSections() , OnSectionChanged() , OnGenerateCustomNameWidgets() , OnGenerateCustomSectionWidgets() , OnResetSectionToDefaultClicked() {} /** Delegate called to populate the list with Sections */ FOnGetSections OnGetSections; /** Delegate called when a user changes the Section */ FOnSectionChanged OnSectionChanged; /** Delegate called to generate custom widgets under the name of in the left column of a details panel*/ FOnGenerateWidgetsForSection OnGenerateCustomNameWidgets; /** Delegate called to generate custom widgets under each Section */ FOnGenerateWidgetsForSection OnGenerateCustomSectionWidgets; /** Delegate called when a Section list item should be reset to default */ FOnResetSectionToDefaultClicked OnResetSectionToDefaultClicked; /** Delegate called Copying a section list */ FOnCopySectionList OnCopySectionList; /** Delegate called to know if we can copy a section list */ FOnCanCopySectionList OnCanCopySectionList; /** Delegate called Pasting a section list */ FOnPasteSectionList OnPasteSectionList; /** Delegate called Copying a section item */ FOnCopySectionItem OnCopySectionItem; /** Delegate called To know if we can copy a section item */ FOnCanCopySectionItem OnCanCopySectionItem; /** Delegate called Pasting a section item */ FOnPasteSectionItem OnPasteSectionItem; /** Delegate called when enabling/disabling a section item */ FOnEnableSectionItem OnEnableSectionItem; }; /** * Builds up a list of unique Sections while creating some information about the Sections */ class ISectionListBuilder { public: virtual ~ISectionListBuilder() {}; /** * Adds a new Section to the list * * @param SlotIndex The slot (usually mesh element index) where the Section is located on the component * @param Section The Section being used * @param bCanBeReplced Whether or not the Section can be replaced by a user */ virtual void AddSection(int32 LodIndex, int32 SectionIndex, FName InMaterialSlotName, int32 InMaterialSlotIndex, FName InOriginalMaterialSlotName, const TMap &InAvailableMaterialSlotName, const UMaterialInterface* Material, bool IsSectionUsingCloth, bool bIsChunkSection, int32 DefaultMaterialIndex) = 0; }; /** * A Section item in a Section list slot */ struct FSectionListItem { /** LodIndex of the Section*/ int32 LodIndex; /** Section index */ int32 SectionIndex; /* Is this section is using cloth */ bool IsSectionUsingCloth; /* Size of the preview material thumbnail */ int32 ThumbnailSize; /** Material being readonly view in the list */ TWeakObjectPtr Material; /* Material Slot Name */ FName MaterialSlotName; int32 MaterialSlotIndex; FName OriginalMaterialSlotName; /* Available material slot name*/ TMap AvailableMaterialSlotName; bool bIsChunkSection; int32 DefaultMaterialIndex; FSectionListItem(int32 InLodIndex, int32 InSectionIndex, FName InMaterialSlotName, int32 InMaterialSlotIndex, FName InOriginalMaterialSlotName, const TMap &InAvailableMaterialSlotName, const UMaterialInterface* InMaterial, bool InIsSectionUsingCloth, int32 InThumbnailSize, bool InIsChunkSection, int32 InDefaultMaterialIndex) : LodIndex(InLodIndex) , SectionIndex(InSectionIndex) , IsSectionUsingCloth(InIsSectionUsingCloth) , ThumbnailSize(InThumbnailSize) , Material(const_cast(InMaterial)) , MaterialSlotName(InMaterialSlotName) , MaterialSlotIndex(InMaterialSlotIndex) , OriginalMaterialSlotName(InOriginalMaterialSlotName) , AvailableMaterialSlotName(InAvailableMaterialSlotName) , bIsChunkSection(InIsChunkSection) , DefaultMaterialIndex(InDefaultMaterialIndex) {} bool operator==(const FSectionListItem& Other) const { bool IsSectionItemEqual = LodIndex == Other.LodIndex && SectionIndex == Other.SectionIndex && MaterialSlotIndex == Other.MaterialSlotIndex && MaterialSlotName == Other.MaterialSlotName && Material == Other.Material && AvailableMaterialSlotName.Num() == Other.AvailableMaterialSlotName.Num() && IsSectionUsingCloth == Other.IsSectionUsingCloth && bIsChunkSection == Other.bIsChunkSection && DefaultMaterialIndex == Other.DefaultMaterialIndex; if (IsSectionItemEqual) { for (auto Kvp : AvailableMaterialSlotName) { if (!Other.AvailableMaterialSlotName.Contains(Kvp.Key)) { IsSectionItemEqual = false; break; } FName OtherName = *(Other.AvailableMaterialSlotName.Find(Kvp.Key)); if (Kvp.Value != OtherName) { IsSectionItemEqual = false; break; } } } return IsSectionItemEqual; } bool operator!=(const FSectionListItem& Other) const { return !(*this == Other); } }; class FSectionList : public IDetailCustomNodeBuilder, public TSharedFromThis { public: PROPERTYEDITOR_API FSectionList(IDetailLayoutBuilder& InDetailLayoutBuilder, FSectionListDelegates& SectionListDelegates, bool bInInitiallyCollapsed, int32 InThumbnailSize, int32 InSectionsLodIndex, FName InSectionListName); /** * @return true if Sections are being displayed */ bool IsDisplayingSections() const { return true; } private: /** * Called when a user expands all materials in a slot */ void OnDisplaySectionsForLod(int32 LodIndex); /** * Called when a user hides all materials in a slot */ void OnHideSectionsForLod(int32 LodIndex); /** IDetailCustomNodeBuilder interface */ virtual void SetOnRebuildChildren(FSimpleDelegate InOnRebuildChildren) override { OnRebuildChildren = InOnRebuildChildren; } virtual bool RequiresTick() const override { return true; } virtual void Tick(float DeltaTime) override; virtual void GenerateHeaderRowContent(FDetailWidgetRow& NodeRow) override; virtual void GenerateChildContent(IDetailChildrenBuilder& ChildrenBuilder) override; virtual FName GetName() const override { return SectionListName; } virtual bool InitiallyCollapsed() const override { return bInitiallyCollapsed; } /** * Adds a new Section item to the list * * @param Row The row to add the item to * @param LodIndex The Lod of the section * @param Item The Section item to add * @param bDisplayLink If a link to the Section should be displayed instead of the actual item (for multiple Sections) */ void AddSectionItem(class FDetailWidgetRow& Row, int32 LodIndex, const struct FSectionListItem& Item, bool bDisplayLink); private: bool OnCanCopySectionList() const; void OnCopySectionList(); void OnPasteSectionList(); bool OnCanCopySectionItem(int32 LODIndex, int32 SectionIndex) const; void OnCopySectionItem(int32 LODIndex, int32 SectionIndex); void OnPasteSectionItem(int32 LODIndex, int32 SectionIndex); void OnEnableSectionItem(int32 LodIndex, int32 SectionIndex, bool bEnable); /** Delegates for the Section list */ FSectionListDelegates SectionListDelegates; /** Called to rebuild the children of the detail tree */ FSimpleDelegate OnRebuildChildren; /** Parent detail layout this list is in */ IDetailLayoutBuilder& DetailLayoutBuilder; /** Set of all unique displayed Sections */ TArray< FSectionListItem > DisplayedSections; /** Set of all Sections currently in view (may be less than DisplayedSections) */ TArray< TSharedRef > ViewedSections; /** Set of all expanded slots */ TSet ExpandedSlots; /** Section list builder used to generate Sections */ TSharedRef SectionListBuilder; /** Set the initial state of the collapse. */ bool bInitiallyCollapsed; FName SectionListName; int32 ThumbnailSize; int32 SectionsLodIndex; };