// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "IDetailPropertyRow.h" #include "PropertyHandle.h" #include "Framework/Commands/UIAction.h" #include "Layout/Visibility.h" #include "Misc/Attribute.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SWidget.h" #include "Widgets/Layout/SSpacer.h" #include "DetailCategoryBuilder.h" #include "PropertyEditorCopyPaste.h" class FDetailWidgetRow; class FResetToDefaultOverride; class IDetailDragDropHandler; /** Widget declaration for custom widgets in a widget row */ class FDetailWidgetDecl { public: FDetailWidgetDecl( class FDetailWidgetRow& InParentDecl, float InMinWidth, float InMaxWidth, EHorizontalAlignment InHAlign, EVerticalAlignment InVAlign ) : Widget( SNew( SInvalidDetailWidget ) ) , HorizontalAlignment( InHAlign ) , VerticalAlignment( InVAlign ) , MinWidth( InMinWidth ) , MaxWidth( InMaxWidth ) , ParentDecl( &InParentDecl ) { } FDetailWidgetDecl( class FDetailWidgetRow& InParentDecl, const FDetailWidgetDecl& Other ) : Widget( Other.Widget ) , HorizontalAlignment( Other.HorizontalAlignment ) , VerticalAlignment( Other.VerticalAlignment ) , MinWidth( Other.MinWidth ) , MaxWidth( Other.MaxWidth ) , ParentDecl( &InParentDecl ) { } FDetailWidgetRow& operator[]( TSharedRef InWidget ) { Widget = InWidget; return *ParentDecl; } FDetailWidgetDecl& VAlign( EVerticalAlignment InAlignment ) { VerticalAlignment = InAlignment; return *this; } FDetailWidgetDecl& HAlign( EHorizontalAlignment InAlignment ) { HorizontalAlignment = InAlignment; return *this; } FDetailWidgetDecl& MinDesiredWidth( TOptional InMinWidth ) { MinWidth = InMinWidth; return *this; } FDetailWidgetDecl& MaxDesiredWidth( TOptional InMaxWidth ) { MaxWidth = InMaxWidth; return *this; } private: class SInvalidDetailWidget : public SSpacer { SLATE_BEGIN_ARGS( SInvalidDetailWidget ) {} SLATE_END_ARGS() void Construct( const FArguments& InArgs ) { SetVisibility(EVisibility::Collapsed); } }; public: TSharedRef Widget; EHorizontalAlignment HorizontalAlignment; EVerticalAlignment VerticalAlignment; TOptional MinWidth; TOptional MaxWidth; private: class FDetailWidgetRow* ParentDecl; }; static FName InvalidDetailWidgetName = TEXT("SInvalidDetailWidget"); /** * Represents a single row of custom widgets in a details panel */ class FDetailWidgetRow : public IDetailLayoutRow { public: PROPERTYEDITOR_API const static float DefaultValueMinWidth; PROPERTYEDITOR_API const static float DefaultValueMaxWidth; FDetailWidgetRow() : NameWidget( *this, 0.0f, 0.0f, HAlign_Left, VAlign_Center ) , ValueWidget( *this, DefaultValueMinWidth, DefaultValueMaxWidth, HAlign_Left, VAlign_Center ) , ExtensionWidget( *this, 0.0f, 0.0f, HAlign_Right, VAlign_Center) , ResetToDefaultWidget( *this, 0.0f, 0.0f, HAlign_Center, VAlign_Center) , WholeRowWidget( *this, 0.0f, 0.0f, HAlign_Fill, VAlign_Fill ) , VisibilityAttr( EVisibility::Visible ) , FilterTextString() , CopyMenuAction() , PasteMenuAction() , RowTagName() { } FDetailWidgetRow& operator=(const FDetailWidgetRow& Other) { NameWidget = FDetailWidgetDecl(*this, Other.NameWidget); ValueWidget = FDetailWidgetDecl(*this, Other.ValueWidget); ExtensionWidget = FDetailWidgetDecl(*this, Other.ExtensionWidget); ResetToDefaultWidget = FDetailWidgetDecl(*this, Other.ResetToDefaultWidget); WholeRowWidget = FDetailWidgetDecl(*this, Other.WholeRowWidget); VisibilityAttr = Other.VisibilityAttr; IsEnabledAttr = Other.IsEnabledAttr; IsValueEnabledAttr = Other.IsValueEnabledAttr; FilterTextString = Other.FilterTextString; CopyMenuAction = Other.CopyMenuAction; PasteMenuAction = Other.PasteMenuAction; CustomMenuItems = Other.CustomMenuItems; RowTagName = Other.RowTagName; CustomResetToDefault = Other.CustomResetToDefault; EditConditionValue = Other.EditConditionValue; OnEditConditionValueChanged = Other.OnEditConditionValueChanged; CustomDragDropHandler = Other.CustomDragDropHandler; PropertyHandles = Other.PropertyHandles; return *this; } virtual ~FDetailWidgetRow() {} /** IDetailLayoutRow interface */ virtual FName GetRowName() const override { return RowTagName; } virtual TOptional GetCustomResetToDefault() const override { return CustomResetToDefault; } /** * Assigns content to the entire row */ FDetailWidgetRow& operator[]( TSharedRef InWidget ) { WholeRowWidget.Widget = InWidget; return *this; } /** * Assigns content to the whole slot, this is an explicit alternative to using [] */ FDetailWidgetDecl& WholeRowContent() { return WholeRowWidget; } /** * Assigns content to just the name slot */ FDetailWidgetDecl& NameContent() { return NameWidget; } /** * Assigns content to the value slot */ FDetailWidgetDecl& ValueContent() { return ValueWidget; } /** * Assigns content to the extension (right) slot */ FDetailWidgetDecl& ExtensionContent() { return ExtensionWidget; } /** * Assigns content to the reset to default (right-most) slot */ FDetailWidgetDecl& ResetToDefaultContent() { return ResetToDefaultWidget; } /** * Sets a string which should be used to filter the content when a user searches */ FDetailWidgetRow& FilterString( const FText& InFilterString ) { FilterTextString = InFilterString; return *this; } /** * Sets the visibility of the entire row */ FDetailWidgetRow& Visibility( const TAttribute& InVisibility ) { VisibilityAttr = InVisibility; return *this; } /** * Sets the enabled state of the entire row */ FDetailWidgetRow& IsEnabled( const TAttribute& InIsEnabled ) { IsEnabledAttr = InIsEnabled; return *this; } /** * Sets the enabled state of the value widget only */ FDetailWidgetRow& IsValueEnabled( const TAttribute& InIsEnabled ) { IsValueEnabledAttr = InIsEnabled; return *this; } /** * Sets a custom copy action to take when copying the data from this row */ FDetailWidgetRow& CopyAction( const FUIAction& InCopyAction ) { CopyMenuAction = InCopyAction; return *this; } /** * Sets a custom paste action to take when copying the data from this row */ FDetailWidgetRow& PasteAction( const FUIAction& InPasteAction ) { PasteMenuAction = InPasteAction; return *this; } /** * Add a custom action to the row context menu */ FDetailWidgetRow& AddCustomContextMenuAction(const FUIAction& Action, const FText& Name, const FText& Tooltip = FText(), const FSlateIcon& SlateIcon = FSlateIcon()) { CustomMenuItems.Add(FCustomMenuData(Action, Name, Tooltip, SlateIcon)); return *this; } bool HasNameContent() const { return NameWidget.Widget->GetType() != InvalidDetailWidgetName; } bool HasValueContent() const { return ValueWidget.Widget->GetType() != InvalidDetailWidgetName; } bool HasExtensionContent() const { return ExtensionWidget.Widget->GetType() != InvalidDetailWidgetName; } bool HasResetToDefaultContent() const { return ResetToDefaultWidget.Widget->GetType() != InvalidDetailWidgetName; } /** * @return true if the row has columns, false if it spans the entire row */ bool HasColumns() const { return HasNameContent() || HasValueContent(); } /** * @return true of the row has any content */ bool HasAnyContent() const { return WholeRowWidget.Widget->GetType() != InvalidDetailWidgetName || HasColumns() || HasResetToDefaultContent(); } /** @return true if a custom copy/paste is bound on this row */ bool IsCopyPasteBound() const { return CopyMenuAction.ExecuteAction.IsBound() && PasteMenuAction.ExecuteAction.IsBound(); } /** @return true if a custom paste text is bound on this row */ bool IsPasteFromTextBound() const { return OnPasteFromTextDelegate.IsValid() && OnPasteFromTextDelegate.Pin()->IsBoundToObject(this); } /** * Sets a tag which can be used to identify this row */ FDetailWidgetRow& RowTag(const FName& InRowTagName) { RowTagName = InRowTagName; return *this; } /** * Sets flag to indicate if property value differs from the default */ FDetailWidgetRow& OverrideResetToDefault(const FResetToDefaultOverride& InResetToDefaultOverride) { CustomResetToDefault = InResetToDefaultOverride; return *this; } /** * Override the edit condition. */ FDetailWidgetRow& EditCondition(TAttribute InEditConditionValue, FOnBooleanValueChanged InOnEditConditionValueChanged) { EditConditionValue = InEditConditionValue; OnEditConditionValueChanged = InOnEditConditionValueChanged; return *this; } /** * Sets a handler for the row to be a source or target of drag-and-drop operations. */ FDetailWidgetRow& DragDropHandler(TSharedPtr InDragDropHandler) { CustomDragDropHandler = InDragDropHandler; return *this; } /** * Used to provide all the property handles this WidgetRow represent */ FDetailWidgetRow& PropertyHandleList(const TArray>& InPropertyHandles) { PropertyHandles = InPropertyHandles; return *this; } /** * Return all the property handles this WidgetRow represent */ const TArray>& GetPropertyHandles() const { return PropertyHandles; } /** * Sets whether or not this property should auto-expand * * @param bForceExpansion true to force the property to be expanded */ FDetailWidgetRow& ShouldAutoExpand(bool bForceExpansion = true) { ForceAutoExpansion = bForceExpansion; return *this; } public: /** Name column content */ FDetailWidgetDecl NameWidget; /** Value column content */ FDetailWidgetDecl ValueWidget; /** Extension (right) column content */ FDetailWidgetDecl ExtensionWidget; /** Reset to default (right-most) column content */ FDetailWidgetDecl ResetToDefaultWidget; /** Whole row content */ FDetailWidgetDecl WholeRowWidget; /** Visibility of the row */ TAttribute VisibilityAttr; /** IsEnabled of the row */ TAttribute IsEnabledAttr; /** IsEnabled of the value widget only */ TAttribute IsValueEnabledAttr; /** String to filter with */ FText FilterTextString; /** Action for copying data on this row */ FUIAction CopyMenuAction; /** Action for pasting data on this row */ FUIAction PasteMenuAction; /** Delegate for pasting (optionally tagged) text on this row */ TWeakPtr OnPasteFromTextDelegate; struct FCustomMenuData { FCustomMenuData(const FUIAction& InAction, const FText& InName, const FText& InTooltip, const FSlateIcon& InSlateIcon) : Action(InAction) , Name(InName) , Tooltip(InTooltip) , SlateIcon(InSlateIcon) {} FCustomMenuData(const FName InEntryName, const FUIAction& InAction, const FText& InName, const FText& InTooltip, const FSlateIcon& InSlateIcon) : EntryName(InEntryName) , Action(InAction) , Name(InName) , Tooltip(InTooltip) , SlateIcon(InSlateIcon) {} const FName EntryName; const FUIAction Action; const FText Name; const FText Tooltip; const FSlateIcon SlateIcon; /** Returns the EntryName if specified, otherwise derive from Name. */ const FName GetEntryName() const; }; /** Custom Action on this row */ TArray CustomMenuItems; /* Tag to identify this row */ FName RowTagName; /** Custom reset to default handler */ TOptional CustomResetToDefault; /** Custom edit condition value. */ TAttribute EditConditionValue; /** Custom edit condition value changed handler. */ FOnBooleanValueChanged OnEditConditionValueChanged; /** Custom handler for drag-and-drop of the row */ TSharedPtr CustomDragDropHandler; /* All property handle that this custom widget represent */ TArray> PropertyHandles; /** True to force auto-expansion */ TOptional ForceAutoExpansion; };