Files
UnrealEngine/Engine/Source/Editor/PropertyEditor/Public/DetailWidgetRow.h
2025-05-18 13:04:45 +08:00

445 lines
12 KiB
C++

// 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<SWidget> InWidget )
{
Widget = InWidget;
return *ParentDecl;
}
FDetailWidgetDecl& VAlign( EVerticalAlignment InAlignment )
{
VerticalAlignment = InAlignment;
return *this;
}
FDetailWidgetDecl& HAlign( EHorizontalAlignment InAlignment )
{
HorizontalAlignment = InAlignment;
return *this;
}
FDetailWidgetDecl& MinDesiredWidth( TOptional<float> InMinWidth )
{
MinWidth = InMinWidth;
return *this;
}
FDetailWidgetDecl& MaxDesiredWidth( TOptional<float> 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<SWidget> Widget;
EHorizontalAlignment HorizontalAlignment;
EVerticalAlignment VerticalAlignment;
TOptional<float> MinWidth;
TOptional<float> 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<FResetToDefaultOverride> GetCustomResetToDefault() const override { return CustomResetToDefault; }
/**
* Assigns content to the entire row
*/
FDetailWidgetRow& operator[]( TSharedRef<SWidget> 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<EVisibility>& InVisibility )
{
VisibilityAttr = InVisibility;
return *this;
}
/**
* Sets the enabled state of the entire row
*/
FDetailWidgetRow& IsEnabled( const TAttribute<bool>& InIsEnabled )
{
IsEnabledAttr = InIsEnabled;
return *this;
}
/**
* Sets the enabled state of the value widget only
*/
FDetailWidgetRow& IsValueEnabled( const TAttribute<bool>& 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<bool> 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<IDetailDragDropHandler> InDragDropHandler)
{
CustomDragDropHandler = InDragDropHandler;
return *this;
}
/**
* Used to provide all the property handles this WidgetRow represent
*/
FDetailWidgetRow& PropertyHandleList(const TArray<TSharedPtr<IPropertyHandle>>& InPropertyHandles)
{
PropertyHandles = InPropertyHandles;
return *this;
}
/**
* Return all the property handles this WidgetRow represent
*/
const TArray<TSharedPtr<IPropertyHandle>>& 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<EVisibility> VisibilityAttr;
/** IsEnabled of the row */
TAttribute<bool> IsEnabledAttr;
/** IsEnabled of the value widget only */
TAttribute<bool> 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<FOnPasteFromText> 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<FCustomMenuData> CustomMenuItems;
/* Tag to identify this row */
FName RowTagName;
/** Custom reset to default handler */
TOptional<FResetToDefaultOverride> CustomResetToDefault;
/** Custom edit condition value. */
TAttribute<bool> EditConditionValue;
/** Custom edit condition value changed handler. */
FOnBooleanValueChanged OnEditConditionValueChanged;
/** Custom handler for drag-and-drop of the row */
TSharedPtr<IDetailDragDropHandler> CustomDragDropHandler;
/* All property handle that this custom widget represent */
TArray<TSharedPtr<IPropertyHandle>> PropertyHandles;
/** True to force auto-expansion */
TOptional<bool> ForceAutoExpansion;
};