2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "DetailCustomizationsPrivatePCH.h"
2014-05-22 11:33:54 -04:00
# include "Materials/MaterialExpressionConstant3Vector.h"
2014-03-14 14:13:41 -04:00
# include "MathStructCustomizations.h"
2014-06-18 06:40:19 -04:00
# include "IPropertyUtilities.h"
2014-08-27 20:35:19 -04:00
# include "SColorPicker.h"
2014-10-14 22:50:06 -04:00
# include "SNumericEntryBox.h"
2014-08-27 20:35:19 -04:00
2014-03-14 14:13:41 -04:00
2014-04-23 17:45:47 -04:00
# define LOCTEXT_NAMESPACE "FMathStructCustomization"
2014-08-27 20:35:19 -04:00
2014-06-04 10:16:14 -04:00
TSharedRef < IPropertyTypeCustomization > FMathStructCustomization : : MakeInstance ( )
2014-03-14 14:13:41 -04:00
{
return MakeShareable ( new FMathStructCustomization ) ;
}
2014-06-04 11:16:24 -04:00
void FMathStructCustomization : : CustomizeHeader ( TSharedRef < class IPropertyHandle > StructPropertyHandle , class FDetailWidgetRow & HeaderRow , IPropertyTypeCustomizationUtils & StructCustomizationUtils )
2014-03-14 14:13:41 -04:00
{
GetSortedChildren ( StructPropertyHandle , SortedChildHandles ) ;
MakeHeaderRow ( StructPropertyHandle , HeaderRow ) ;
}
2014-06-04 11:16:24 -04:00
void FMathStructCustomization : : CustomizeChildren ( TSharedRef < class IPropertyHandle > StructPropertyHandle , class IDetailChildrenBuilder & StructBuilder , IPropertyTypeCustomizationUtils & StructCustomizationUtils )
2014-03-14 14:13:41 -04:00
{
for ( int32 ChildIndex = 0 ; ChildIndex < SortedChildHandles . Num ( ) ; + + ChildIndex )
{
TSharedRef < IPropertyHandle > ChildHandle = SortedChildHandles [ ChildIndex ] ;
// Add the individual properties as children as well so the vector can be expanded for more room
StructBuilder . AddChildProperty ( ChildHandle ) ;
}
}
2014-04-02 18:09:23 -04:00
2014-03-14 14:13:41 -04:00
void FMathStructCustomization : : MakeHeaderRow ( TSharedRef < class IPropertyHandle > & StructPropertyHandle , FDetailWidgetRow & Row )
{
// We'll set up reset to default ourselves
const bool bDisplayResetToDefault = false ;
2014-12-01 11:19:41 -05:00
const FText DisplayNameOverride = FText : : GetEmpty ( ) ;
const FText DisplayToolTipOverride = FText : : GetEmpty ( ) ;
2014-03-14 14:13:41 -04:00
2014-04-02 18:09:23 -04:00
TWeakPtr < IPropertyHandle > StructWeakHandlePtr = StructPropertyHandle ;
2014-03-14 14:13:41 -04:00
TSharedPtr < SHorizontalBox > HorizontalBox ;
Row . NameContent ( )
[
2014-10-21 11:13:59 -04:00
StructPropertyHandle - > CreatePropertyNameWidget ( DisplayNameOverride , DisplayToolTipOverride , bDisplayResetToDefault )
2014-03-14 14:13:41 -04:00
]
. ValueContent ( )
// Make enough space for each child handle
. MinDesiredWidth ( 125.0f * SortedChildHandles . Num ( ) )
. MaxDesiredWidth ( 125.0f * SortedChildHandles . Num ( ) )
[
SAssignNew ( HorizontalBox , SHorizontalBox )
2014-04-02 18:09:23 -04:00
. IsEnabled ( this , & FMathStructCustomization : : IsValueEnabled , StructWeakHandlePtr )
2014-03-14 14:13:41 -04:00
] ;
for ( int32 ChildIndex = 0 ; ChildIndex < SortedChildHandles . Num ( ) ; + + ChildIndex )
{
TSharedRef < IPropertyHandle > ChildHandle = SortedChildHandles [ ChildIndex ] ;
const bool bLastChild = SortedChildHandles . Num ( ) - 1 = = ChildIndex ;
// Make a widget for each property. The vector component properties will be displayed in the header
HorizontalBox - > AddSlot ( )
. Padding ( FMargin ( 0.0f , 2.0f , bLastChild ? 0.0f : 3.0f , 2.0f ) )
[
2014-09-04 17:26:33 -04:00
MakeChildWidget ( StructPropertyHandle , ChildHandle )
2014-03-14 14:13:41 -04:00
] ;
}
2014-04-23 17:45:47 -04:00
if ( StructPropertyHandle - > GetProperty ( ) - > HasMetaData ( " AllowPreserveRatio " ) )
{
2015-04-20 10:12:55 -04:00
if ( ! GConfig - > GetBool ( TEXT ( " SelectionDetails " ) , * ( StructPropertyHandle - > GetProperty ( ) - > GetName ( ) + TEXT ( " _PreserveScaleRatio " ) ) , bPreserveScaleRatio , GEditorPerProjectIni ) )
2014-04-23 17:45:47 -04:00
{
bPreserveScaleRatio = true ;
}
HorizontalBox - > AddSlot ( )
. AutoWidth ( )
. MaxWidth ( 18.0f )
[
// Add a checkbox to toggle between preserving the ratio of x,y,z components of scale when a value is entered
SNew ( SCheckBox )
. IsChecked ( this , & FMathStructCustomization : : IsPreserveScaleRatioChecked )
. OnCheckStateChanged ( this , & FMathStructCustomization : : OnPreserveScaleRatioToggled , StructWeakHandlePtr )
. Style ( FEditorStyle : : Get ( ) , " TransparentCheckBox " )
. ToolTipText ( LOCTEXT ( " PreserveScaleToolTip " , " When locked, scales uniformly based on the current xyz scale values so the object maintains its shape in each direction when scaled " ) )
[
SNew ( SImage )
. Image ( this , & FMathStructCustomization : : GetPreserveScaleRatioImage )
. ColorAndOpacity ( FSlateColor : : UseForeground ( ) )
]
] ;
}
}
const FSlateBrush * FMathStructCustomization : : GetPreserveScaleRatioImage ( ) const
{
return bPreserveScaleRatio ? FEditorStyle : : GetBrush ( TEXT ( " GenericLock " ) ) : FEditorStyle : : GetBrush ( TEXT ( " GenericUnlock " ) ) ;
}
2014-12-10 14:24:09 -05:00
ECheckBoxState FMathStructCustomization : : IsPreserveScaleRatioChecked ( ) const
2014-04-23 17:45:47 -04:00
{
2014-12-10 14:24:09 -05:00
return bPreserveScaleRatio ? ECheckBoxState : : Checked : ECheckBoxState : : Unchecked ;
2014-04-23 17:45:47 -04:00
}
2014-12-10 14:24:09 -05:00
void FMathStructCustomization : : OnPreserveScaleRatioToggled ( ECheckBoxState NewState , TWeakPtr < IPropertyHandle > PropertyHandle )
2014-04-23 17:45:47 -04:00
{
2014-12-10 14:24:09 -05:00
bPreserveScaleRatio = ( NewState = = ECheckBoxState : : Checked ) ? true : false ;
2014-04-23 17:45:47 -04:00
if ( PropertyHandle . IsValid ( ) )
{
FString SettingKey = ( PropertyHandle . Pin ( ) - > GetProperty ( ) - > GetName ( ) + TEXT ( " _PreserveScaleRatio " ) ) ;
2015-04-20 10:12:55 -04:00
GConfig - > SetBool ( TEXT ( " SelectionDetails " ) , * SettingKey , bPreserveScaleRatio , GEditorPerProjectIni ) ;
2014-04-23 17:45:47 -04:00
}
2014-03-14 14:13:41 -04:00
}
void FMathStructCustomization : : GetSortedChildren ( TSharedRef < IPropertyHandle > StructPropertyHandle , TArray < TSharedRef < IPropertyHandle > > & OutChildren )
{
OutChildren . Empty ( ) ;
uint32 NumChildren ;
StructPropertyHandle - > GetNumChildren ( NumChildren ) ;
for ( uint32 ChildIndex = 0 ; ChildIndex < NumChildren ; + + ChildIndex )
{
OutChildren . Add ( StructPropertyHandle - > GetChildHandle ( ChildIndex ) . ToSharedRef ( ) ) ;
}
}
template < typename NumericType >
2014-09-04 17:26:33 -04:00
void ExtractNumericMetadata ( TSharedRef < IPropertyHandle > & PropertyHandle , TOptional < NumericType > & MinValue , TOptional < NumericType > & MaxValue , TOptional < NumericType > & SliderMinValue , TOptional < NumericType > & SliderMaxValue , NumericType & SliderExponent , NumericType & Delta )
2014-03-14 14:13:41 -04:00
{
2014-09-04 17:26:33 -04:00
UProperty * Property = PropertyHandle - > GetProperty ( ) ;
const FString & MetaUIMinString = Property - > GetMetaData ( TEXT ( " UIMin " ) ) ;
const FString & MetaUIMaxString = Property - > GetMetaData ( TEXT ( " UIMax " ) ) ;
const FString & SliderExponentString = Property - > GetMetaData ( TEXT ( " SliderExponent " ) ) ;
const FString & DeltaString = Property - > GetMetaData ( TEXT ( " Delta " ) ) ;
const FString & ClampMinString = Property - > GetMetaData ( TEXT ( " ClampMin " ) ) ;
const FString & ClampMaxString = Property - > GetMetaData ( TEXT ( " ClampMax " ) ) ;
// If no UIMin/Max was specified then use the clamp string
const FString & UIMinString = MetaUIMinString . Len ( ) ? MetaUIMinString : ClampMinString ;
const FString & UIMaxString = MetaUIMaxString . Len ( ) ? MetaUIMaxString : ClampMaxString ;
NumericType ClampMin = TNumericLimits < NumericType > : : Lowest ( ) ;
NumericType ClampMax = TNumericLimits < NumericType > : : Max ( ) ;
if ( ! ClampMinString . IsEmpty ( ) )
{
TTypeFromString < NumericType > : : FromString ( ClampMin , * ClampMinString ) ;
}
if ( ! ClampMaxString . IsEmpty ( ) )
{
TTypeFromString < NumericType > : : FromString ( ClampMax , * ClampMaxString ) ;
}
NumericType UIMin = TNumericLimits < NumericType > : : Lowest ( ) ;
NumericType UIMax = TNumericLimits < NumericType > : : Max ( ) ;
TTypeFromString < NumericType > : : FromString ( UIMin , * UIMinString ) ;
TTypeFromString < NumericType > : : FromString ( UIMax , * UIMaxString ) ;
SliderExponent = NumericType ( 1 ) ;
if ( SliderExponentString . Len ( ) )
{
TTypeFromString < NumericType > : : FromString ( SliderExponent , * SliderExponentString ) ;
}
Delta = NumericType ( 0 ) ;
if ( DeltaString . Len ( ) )
{
TTypeFromString < NumericType > : : FromString ( Delta , * DeltaString ) ;
}
if ( ClampMin > = ClampMax & & ( ClampMinString . Len ( ) | | ClampMaxString . Len ( ) ) )
{
//UE_LOG(LogPropertyNode, Warning, TEXT("Clamp Min (%s) >= Clamp Max (%s) for Ranged Numeric"), *ClampMinString, *ClampMaxString);
}
const NumericType ActualUIMin = FMath : : Max ( UIMin , ClampMin ) ;
const NumericType ActualUIMax = FMath : : Min ( UIMax , ClampMax ) ;
MinValue = ClampMinString . Len ( ) ? ClampMin : TOptional < NumericType > ( ) ;
MaxValue = ClampMaxString . Len ( ) ? ClampMax : TOptional < NumericType > ( ) ;
SliderMinValue = ( UIMinString . Len ( ) ) ? ActualUIMin : TOptional < NumericType > ( ) ;
SliderMaxValue = ( UIMaxString . Len ( ) ) ? ActualUIMax : TOptional < NumericType > ( ) ;
if ( ActualUIMin > = ActualUIMax & & ( MetaUIMinString . Len ( ) | | MetaUIMaxString . Len ( ) ) )
{
//UE_LOG(LogPropertyNode, Warning, TEXT("UI Min (%s) >= UI Max (%s) for Ranged Numeric"), *UIMinString, *UIMaxString);
}
}
template < typename NumericType >
TSharedRef < SWidget > FMathStructCustomization : : MakeNumericWidget (
TSharedRef < IPropertyHandle > & StructurePropertyHandle ,
TSharedRef < IPropertyHandle > & PropertyHandle )
{
TOptional < NumericType > MinValue , MaxValue , SliderMinValue , SliderMaxValue ;
NumericType SliderExponent , Delta ;
ExtractNumericMetadata ( StructurePropertyHandle , MinValue , MaxValue , SliderMinValue , SliderMaxValue , SliderExponent , Delta ) ;
2014-03-14 14:13:41 -04:00
TWeakPtr < IPropertyHandle > WeakHandlePtr = PropertyHandle ;
return
SNew ( SNumericEntryBox < NumericType > )
2014-04-02 18:09:23 -04:00
. IsEnabled ( this , & FMathStructCustomization : : IsValueEnabled , WeakHandlePtr )
2014-03-14 14:13:41 -04:00
. Value ( this , & FMathStructCustomization : : OnGetValue , WeakHandlePtr )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
2014-12-02 06:47:27 -05:00
. UndeterminedString ( NSLOCTEXT ( " PropertyEditor " , " MultipleValues " , " Multiple Values " ) )
2014-03-14 14:13:41 -04:00
. OnValueCommitted ( this , & FMathStructCustomization : : OnValueCommitted < NumericType > , WeakHandlePtr )
. OnValueChanged ( this , & FMathStructCustomization : : OnValueChanged < NumericType > , WeakHandlePtr )
. OnBeginSliderMovement ( this , & FMathStructCustomization : : OnBeginSliderMovement )
. OnEndSliderMovement ( this , & FMathStructCustomization : : OnEndSliderMovement < NumericType > )
. LabelVAlign ( VAlign_Center )
// Only allow spin on handles with one object. Otherwise it is not clear what value to spin
. AllowSpin ( PropertyHandle - > GetNumOuterObjects ( ) = = 1 )
2014-09-04 17:26:33 -04:00
. MinValue ( MinValue )
. MaxValue ( MaxValue )
. MinSliderValue ( SliderMinValue )
. MaxSliderValue ( SliderMaxValue )
. SliderExponent ( SliderExponent )
. Delta ( Delta )
2014-03-14 14:13:41 -04:00
. Label ( )
[
SNew ( STextBlock )
. Font ( IDetailLayoutBuilder : : GetDetailFont ( ) )
. Text ( PropertyHandle - > GetPropertyDisplayName ( ) )
] ;
}
2014-09-04 17:26:33 -04:00
TSharedRef < SWidget > FMathStructCustomization : : MakeChildWidget (
TSharedRef < IPropertyHandle > & StructurePropertyHandle ,
TSharedRef < IPropertyHandle > & PropertyHandle )
2014-03-14 14:13:41 -04:00
{
const UClass * PropertyClass = PropertyHandle - > GetPropertyClass ( ) ;
if ( PropertyClass = = UFloatProperty : : StaticClass ( ) )
{
2014-09-04 17:26:33 -04:00
return MakeNumericWidget < float > ( StructurePropertyHandle , PropertyHandle ) ;
2014-03-14 14:13:41 -04:00
}
if ( PropertyClass = = UIntProperty : : StaticClass ( ) )
{
2014-09-04 17:26:33 -04:00
return MakeNumericWidget < int32 > ( StructurePropertyHandle , PropertyHandle ) ;
2014-03-14 14:13:41 -04:00
}
if ( PropertyClass = = UByteProperty : : StaticClass ( ) )
{
2014-09-04 17:26:33 -04:00
return MakeNumericWidget < uint8 > ( StructurePropertyHandle , PropertyHandle ) ;
2014-03-14 14:13:41 -04:00
}
check ( 0 ) ; // Unsupported class
return SNullWidget : : NullWidget ;
}
template < typename NumericType >
TOptional < NumericType > FMathStructCustomization : : OnGetValue ( TWeakPtr < IPropertyHandle > WeakHandlePtr ) const
{
NumericType NumericVal = 0 ;
if ( WeakHandlePtr . Pin ( ) - > GetValue ( NumericVal ) = = FPropertyAccess : : Success )
{
return TOptional < NumericType > ( NumericVal ) ;
}
// Value couldn't be accessed. Return an unset value
return TOptional < NumericType > ( ) ;
}
template < typename NumericType >
void FMathStructCustomization : : OnValueCommitted ( NumericType NewValue , ETextCommit : : Type CommitType , TWeakPtr < IPropertyHandle > WeakHandlePtr )
{
2014-04-23 17:45:47 -04:00
EPropertyValueSetFlags : : Type Flags = EPropertyValueSetFlags : : DefaultFlags ;
SetValue ( NewValue , Flags , WeakHandlePtr ) ;
2014-03-14 14:13:41 -04:00
}
template < typename NumericType >
void FMathStructCustomization : : OnValueChanged ( NumericType NewValue , TWeakPtr < IPropertyHandle > WeakHandlePtr )
{
if ( bIsUsingSlider )
{
EPropertyValueSetFlags : : Type Flags = EPropertyValueSetFlags : : InteractiveChange ;
2014-04-23 17:45:47 -04:00
SetValue ( NewValue , Flags , WeakHandlePtr ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-04-23 17:45:47 -04:00
template < typename NumericType >
void FMathStructCustomization : : SetValue ( NumericType NewValue , EPropertyValueSetFlags : : Type Flags , TWeakPtr < IPropertyHandle > WeakHandlePtr )
{
if ( bPreserveScaleRatio )
{
// Get the value for each object for the modified component
TArray < FString > OldValues ;
if ( WeakHandlePtr . Pin ( ) - > GetPerObjectValues ( OldValues ) = = FPropertyAccess : : Success )
{
// Loop through each object and scale based on the new ratio for each object individually
for ( int32 OutputIndex = 0 ; OutputIndex < OldValues . Num ( ) ; + + OutputIndex )
{
NumericType OldValue ;
TTypeFromString < NumericType > : : FromString ( OldValue , * OldValues [ OutputIndex ] ) ;
// Account for the previous scale being zero. Just set to the new value in that case?
NumericType Ratio = OldValue = = 0 ? NewValue : NewValue / OldValue ;
if ( Ratio = = 0 )
{
Ratio = NewValue ;
}
// Loop through all the child handles (each component of the math struct, like X, Y, Z...etc)
for ( int32 ChildIndex = 0 ; ChildIndex < SortedChildHandles . Num ( ) ; + + ChildIndex )
{
// Ignore scaling our selves.
TSharedRef < IPropertyHandle > ChildHandle = SortedChildHandles [ ChildIndex ] ;
if ( ChildHandle ! = WeakHandlePtr . Pin ( ) )
{
// Get the value for each object.
TArray < FString > ObjectChildValues ;
if ( ChildHandle - > GetPerObjectValues ( ObjectChildValues ) = = FPropertyAccess : : Success )
{
// Individually scale each object's components by the same ratio.
for ( int32 ChildOutputIndex = 0 ; ChildOutputIndex < ObjectChildValues . Num ( ) ; + + ChildOutputIndex )
{
NumericType ChildOldValue ;
TTypeFromString < NumericType > : : FromString ( ChildOldValue , * ObjectChildValues [ ChildOutputIndex ] ) ;
NumericType ChildNewValue = ChildOldValue * Ratio ;
ObjectChildValues [ ChildOutputIndex ] = TTypeToString < NumericType > : : ToSanitizedString ( ChildNewValue ) ;
}
ChildHandle - > SetPerObjectValues ( ObjectChildValues ) ;
}
}
}
}
}
}
WeakHandlePtr . Pin ( ) - > SetValue ( NewValue , Flags ) ;
}
2014-04-02 18:09:23 -04:00
bool FMathStructCustomization : : IsValueEnabled ( TWeakPtr < IPropertyHandle > WeakHandlePtr ) const
{
if ( WeakHandlePtr . IsValid ( ) )
{
return ! WeakHandlePtr . Pin ( ) - > IsEditConst ( ) ;
}
return false ;
}
2014-03-14 14:13:41 -04:00
void FMathStructCustomization : : OnBeginSliderMovement ( )
{
bIsUsingSlider = true ;
GEditor - > BeginTransaction ( NSLOCTEXT ( " FMathStructCustomization " , " SetVectorProperty " , " Set Vector Property " ) ) ;
}
template < typename NumericType >
void FMathStructCustomization : : OnEndSliderMovement ( NumericType NewValue )
{
bIsUsingSlider = false ;
GEditor - > EndTransaction ( ) ;
}
2014-06-04 10:16:14 -04:00
TSharedRef < IPropertyTypeCustomization > FRotatorStructCustomization : : MakeInstance ( )
2014-03-14 14:13:41 -04:00
{
return MakeShareable ( new FRotatorStructCustomization ) ;
}
void FRotatorStructCustomization : : GetSortedChildren ( TSharedRef < IPropertyHandle > StructPropertyHandle , TArray < TSharedRef < IPropertyHandle > > & OutChildren )
{
static const FName Roll ( " Roll " ) ;
static const FName Pitch ( " Pitch " ) ;
static const FName Yaw ( " Yaw " ) ;
TSharedPtr < IPropertyHandle > RotatorChildren [ 3 ] ;
uint32 NumChildren ;
StructPropertyHandle - > GetNumChildren ( NumChildren ) ;
for ( uint32 ChildIndex = 0 ; ChildIndex < NumChildren ; + + ChildIndex )
{
TSharedRef < IPropertyHandle > ChildHandle = StructPropertyHandle - > GetChildHandle ( ChildIndex ) . ToSharedRef ( ) ;
const FName PropertyName = ChildHandle - > GetProperty ( ) - > GetFName ( ) ;
if ( PropertyName = = Roll )
{
RotatorChildren [ 0 ] = ChildHandle ;
}
else if ( PropertyName = = Pitch )
{
RotatorChildren [ 1 ] = ChildHandle ;
}
else
{
check ( PropertyName = = Yaw ) ;
RotatorChildren [ 2 ] = ChildHandle ;
}
}
OutChildren . Add ( RotatorChildren [ 0 ] . ToSharedRef ( ) ) ;
OutChildren . Add ( RotatorChildren [ 1 ] . ToSharedRef ( ) ) ;
OutChildren . Add ( RotatorChildren [ 2 ] . ToSharedRef ( ) ) ;
}
2014-06-04 10:16:14 -04:00
TSharedRef < IPropertyTypeCustomization > FColorStructCustomization : : MakeInstance ( )
2014-03-14 14:13:41 -04:00
{
return MakeShareable ( new FColorStructCustomization ) ;
}
2014-06-04 11:16:24 -04:00
void FColorStructCustomization : : CustomizeHeader ( TSharedRef < class IPropertyHandle > InStructPropertyHandle , class FDetailWidgetRow & InHeaderRow , IPropertyTypeCustomizationUtils & StructCustomizationUtils )
2014-03-14 14:13:41 -04:00
{
StructPropertyHandle = InStructPropertyHandle ;
bIsLinearColor = CastChecked < UStructProperty > ( StructPropertyHandle - > GetProperty ( ) ) - > Struct - > GetFName ( ) = = NAME_LinearColor ;
bIgnoreAlpha = StructPropertyHandle - > GetProperty ( ) - > HasMetaData ( TEXT ( " HideAlphaChannel " ) ) ;
2014-06-18 06:40:19 -04:00
auto PropertyUtils = StructCustomizationUtils . GetPropertyUtilities ( ) ;
bDontUpdateWhileEditing = PropertyUtils . IsValid ( ) ? PropertyUtils - > DontUpdateValueWhileEditing ( ) : false ;
2014-06-04 10:16:14 -04:00
FMathStructCustomization : : CustomizeHeader ( InStructPropertyHandle , InHeaderRow , StructCustomizationUtils ) ;
2014-03-14 14:13:41 -04:00
}
void FColorStructCustomization : : MakeHeaderRow ( TSharedRef < class IPropertyHandle > & InStructPropertyHandle , FDetailWidgetRow & Row )
{
// We'll set up reset to default ourselves
const bool bDisplayResetToDefault = false ;
2014-12-01 11:19:41 -05:00
const FText DisplayNameOverride = FText : : GetEmpty ( ) ;
const FText DisplayToolTipOverride = FText : : GetEmpty ( ) ;
2014-03-14 14:13:41 -04:00
Row . NameContent ( )
[
2014-10-21 11:13:59 -04:00
StructPropertyHandle - > CreatePropertyNameWidget ( DisplayNameOverride , DisplayToolTipOverride , bDisplayResetToDefault )
2014-03-14 14:13:41 -04:00
]
. ValueContent ( )
. MinDesiredWidth ( 250.0f )
. MaxDesiredWidth ( 250.0f )
[
2014-10-14 13:37:45 -04:00
CreateColorWidget ( )
] ;
}
TSharedRef < SWidget > FColorStructCustomization : : CreateColorWidget ( )
{
FSlateFontInfo NormalText = IDetailLayoutBuilder : : GetDetailFont ( ) ;
return SNew ( SHorizontalBox )
2014-03-14 14:13:41 -04:00
+ SHorizontalBox : : Slot ( )
. VAlign ( VAlign_Center )
[
SNew ( SOverlay )
+ SOverlay : : Slot ( )
[
// Displays the color with alpha unless it is ignored
SAssignNew ( ColorPickerParentWidget , SColorBlock )
. Color ( this , & FColorStructCustomization : : OnGetColorForColorBlock )
. ShowBackgroundForAlpha ( true )
. IgnoreAlpha ( bIgnoreAlpha )
. OnMouseButtonDown ( this , & FColorStructCustomization : : OnMouseButtonDownColorBlock )
. Size ( FVector2D ( 35.0f , 12.0f ) )
]
+ SOverlay : : Slot ( )
. HAlign ( HAlign_Center )
. VAlign ( VAlign_Center )
[
SNew ( STextBlock )
. Text ( NSLOCTEXT ( " PropertyEditor " , " MultipleValues " , " Multiple Values " ) )
. Font ( NormalText )
. ColorAndOpacity ( FSlateColor ( FLinearColor : : Black ) ) // we know the background is always white, so can safely set this to black
. Visibility ( this , & FColorStructCustomization : : GetMultipleValuesTextVisibility )
]
]
+ SHorizontalBox : : Slot ( )
. VAlign ( VAlign_Center )
[
// Displays the color without alpha
SNew ( SColorBlock )
. Color ( this , & FColorStructCustomization : : OnGetColorForColorBlock )
. ShowBackgroundForAlpha ( false )
. IgnoreAlpha ( true )
. OnMouseButtonDown ( this , & FColorStructCustomization : : OnMouseButtonDownColorBlock )
. Size ( FVector2D ( 35.0f , 12.0f ) )
2014-10-14 13:37:45 -04:00
] ;
2014-03-14 14:13:41 -04:00
}
void FColorStructCustomization : : GetSortedChildren ( TSharedRef < IPropertyHandle > InStructPropertyHandle , TArray < TSharedRef < IPropertyHandle > > & OutChildren )
{
static const FName Red ( " R " ) ;
static const FName Green ( " G " ) ;
static const FName Blue ( " B " ) ;
static const FName Alpha ( " A " ) ;
// We control the order of the colors via this array so it always ends up R,G,B,A
TSharedPtr < IPropertyHandle > ColorProperties [ 4 ] ;
uint32 NumChildren ;
InStructPropertyHandle - > GetNumChildren ( NumChildren ) ;
for ( uint32 ChildIndex = 0 ; ChildIndex < NumChildren ; + + ChildIndex )
{
TSharedRef < IPropertyHandle > ChildHandle = InStructPropertyHandle - > GetChildHandle ( ChildIndex ) . ToSharedRef ( ) ;
const FName PropertyName = ChildHandle - > GetProperty ( ) - > GetFName ( ) ;
if ( PropertyName = = Red )
{
ColorProperties [ 0 ] = ChildHandle ;
}
else if ( PropertyName = = Green )
{
ColorProperties [ 1 ] = ChildHandle ;
}
else if ( PropertyName = = Blue )
{
ColorProperties [ 2 ] = ChildHandle ;
}
else
{
ColorProperties [ 3 ] = ChildHandle ;
}
}
OutChildren . Add ( ColorProperties [ 0 ] . ToSharedRef ( ) ) ;
OutChildren . Add ( ColorProperties [ 1 ] . ToSharedRef ( ) ) ;
OutChildren . Add ( ColorProperties [ 2 ] . ToSharedRef ( ) ) ;
// Alpha channel may not be used
if ( ! bIgnoreAlpha & & ColorProperties [ 3 ] . IsValid ( ) )
{
OutChildren . Add ( ColorProperties [ 3 ] . ToSharedRef ( ) ) ;
}
}
void FColorStructCustomization : : CreateColorPicker ( bool bUseAlpha , bool bOnlyRefreshOnOk )
{
int32 NumObjects = StructPropertyHandle - > GetNumOuterObjects ( ) ;
SavedPreColorPickerColors . Empty ( ) ;
TArray < FString > PerObjectValues ;
StructPropertyHandle - > GetPerObjectValues ( PerObjectValues ) ;
for ( int32 ObjectIndex = 0 ; ObjectIndex < NumObjects ; + + ObjectIndex )
{
if ( bIsLinearColor )
{
FLinearColor Color ;
Color . InitFromString ( PerObjectValues [ ObjectIndex ] ) ;
SavedPreColorPickerColors . Add ( Color ) ;
}
else
{
FColor Color ;
Color . InitFromString ( PerObjectValues [ ObjectIndex ] ) ;
SavedPreColorPickerColors . Add ( Color . ReinterpretAsLinear ( ) ) ;
}
}
FLinearColor InitialColor ;
GetColorAsLinear ( InitialColor ) ;
// This needs to be meta data. Other colors could benefit from this
2014-06-18 06:40:19 -04:00
const bool bRefreshOnlyOnOk = bOnlyRefreshOnOk | | StructPropertyHandle - > GetProperty ( ) - > GetOwnerClass ( ) - > IsChildOf ( UMaterialExpressionConstant3Vector : : StaticClass ( ) ) ;
2014-03-14 14:13:41 -04:00
FColorPickerArgs PickerArgs ;
PickerArgs . bUseAlpha = ! bIgnoreAlpha ;
PickerArgs . bOnlyRefreshOnMouseUp = false ;
PickerArgs . bOnlyRefreshOnOk = bRefreshOnlyOnOk ;
PickerArgs . DisplayGamma = TAttribute < float > : : Create ( TAttribute < float > : : FGetter : : CreateUObject ( GEngine , & UEngine : : GetDisplayGamma ) ) ;
PickerArgs . OnColorCommitted = FOnLinearColorValueChanged : : CreateSP ( this , & FColorStructCustomization : : OnSetColorFromColorPicker ) ;
PickerArgs . OnColorPickerCancelled = FOnColorPickerCancelled : : CreateSP ( this , & FColorStructCustomization : : OnColorPickerCancelled ) ;
PickerArgs . OnInteractivePickBegin = FSimpleDelegate : : CreateSP ( this , & FColorStructCustomization : : OnColorPickerInteractiveBegin ) ;
PickerArgs . OnInteractivePickEnd = FSimpleDelegate : : CreateSP ( this , & FColorStructCustomization : : OnColorPickerInteractiveEnd ) ;
PickerArgs . InitialColorOverride = InitialColor ;
PickerArgs . ParentWidget = ColorPickerParentWidget ;
OpenColorPicker ( PickerArgs ) ;
}
TSharedRef < SColorPicker > FColorStructCustomization : : CreateInlineColorPicker ( )
{
int32 NumObjects = StructPropertyHandle - > GetNumOuterObjects ( ) ;
SavedPreColorPickerColors . Empty ( ) ;
TArray < FString > PerObjectValues ;
StructPropertyHandle - > GetPerObjectValues ( PerObjectValues ) ;
for ( int32 ObjectIndex = 0 ; ObjectIndex < NumObjects ; + + ObjectIndex )
{
if ( bIsLinearColor )
{
FLinearColor Color ;
Color . InitFromString ( PerObjectValues [ ObjectIndex ] ) ;
SavedPreColorPickerColors . Add ( Color ) ;
}
else
{
FColor Color ;
Color . InitFromString ( PerObjectValues [ ObjectIndex ] ) ;
SavedPreColorPickerColors . Add ( Color . ReinterpretAsLinear ( ) ) ;
}
}
FLinearColor InitialColor ;
GetColorAsLinear ( InitialColor ) ;
// This needs to be meta data. Other colors could benefit from this
2014-06-18 06:40:19 -04:00
const bool bRefreshOnlyOnOk = bDontUpdateWhileEditing | | StructPropertyHandle - > GetProperty ( ) - > GetOwnerClass ( ) - > IsChildOf ( UMaterialExpressionConstant3Vector : : StaticClass ( ) ) ;
2014-03-14 14:13:41 -04:00
return SNew ( SColorPicker )
. Visibility ( this , & FColorStructCustomization : : GetInlineColorPickerVisibility )
. DisplayInlineVersion ( true )
. OnlyRefreshOnMouseUp ( false )
. OnlyRefreshOnOk ( bRefreshOnlyOnOk )
. DisplayGamma ( TAttribute < float > : : Create ( TAttribute < float > : : FGetter : : CreateUObject ( GEngine , & UEngine : : GetDisplayGamma ) ) )
. OnColorCommitted ( FOnLinearColorValueChanged : : CreateSP ( this , & FColorStructCustomization : : OnSetColorFromColorPicker ) )
. OnColorPickerCancelled ( FOnColorPickerCancelled : : CreateSP ( this , & FColorStructCustomization : : OnColorPickerCancelled ) )
. OnInteractivePickBegin ( FSimpleDelegate : : CreateSP ( this , & FColorStructCustomization : : OnColorPickerInteractiveBegin ) )
. OnInteractivePickEnd ( FSimpleDelegate : : CreateSP ( this , & FColorStructCustomization : : OnColorPickerInteractiveEnd ) )
. TargetColorAttribute ( InitialColor ) ;
}
void FColorStructCustomization : : OnSetColorFromColorPicker ( FLinearColor NewColor )
{
FString ColorString ;
if ( bIsLinearColor )
{
ColorString = NewColor . ToString ( ) ;
}
else
{
// Handled by the color picker
const bool bSRGB = false ;
FColor NewFColor = NewColor . ToFColor ( bSRGB ) ;
ColorString = NewFColor . ToString ( ) ;
}
StructPropertyHandle - > SetValueFromFormattedString ( ColorString , bIsInteractive ? EPropertyValueSetFlags : : InteractiveChange : 0 ) ;
}
void FColorStructCustomization : : OnColorPickerCancelled ( FLinearColor OriginalColor )
{
TArray < FString > PerObjectColors ;
for ( int32 ColorIndex = 0 ; ColorIndex < SavedPreColorPickerColors . Num ( ) ; + + ColorIndex )
{
if ( bIsLinearColor )
{
PerObjectColors . Add ( SavedPreColorPickerColors [ ColorIndex ] . ToString ( ) ) ;
}
else
{
const bool bSRGB = false ;
FColor Color = SavedPreColorPickerColors [ ColorIndex ] . ToFColor ( bSRGB ) ;
PerObjectColors . Add ( Color . ToString ( ) ) ;
}
}
StructPropertyHandle - > SetPerObjectValues ( PerObjectColors ) ;
PerObjectColors . Empty ( ) ;
}
void FColorStructCustomization : : OnColorPickerInteractiveBegin ( )
{
bIsInteractive = true ;
2014-12-01 11:19:41 -05:00
GEditor - > BeginTransaction ( FText : : Format ( NSLOCTEXT ( " FColorStructCustomization " , " SetColorProperty " , " Edit {0} " ) , StructPropertyHandle - > GetPropertyDisplayName ( ) ) ) ;
2014-03-14 14:13:41 -04:00
}
void FColorStructCustomization : : OnColorPickerInteractiveEnd ( )
{
bIsInteractive = false ;
2014-06-18 06:40:19 -04:00
if ( ! bDontUpdateWhileEditing )
{
// pushes the last value from the interactive change without the interactive flag
FString ColorString ;
StructPropertyHandle - > GetValueAsFormattedString ( ColorString ) ;
StructPropertyHandle - > SetValueFromFormattedString ( ColorString ) ;
}
2014-03-14 14:13:41 -04:00
GEditor - > EndTransaction ( ) ;
}
FLinearColor FColorStructCustomization : : OnGetColorForColorBlock ( ) const
{
FLinearColor Color ;
GetColorAsLinear ( Color ) ;
return Color ;
}
FPropertyAccess : : Result FColorStructCustomization : : GetColorAsLinear ( FLinearColor & OutColor ) const
{
// Get each color component
for ( int32 ChildIndex = 0 ; ChildIndex < SortedChildHandles . Num ( ) ; + + ChildIndex )
{
FPropertyAccess : : Result ValueResult = FPropertyAccess : : Fail ;
if ( bIsLinearColor )
{
float ComponentValue = 0 ;
ValueResult = SortedChildHandles [ ChildIndex ] - > GetValue ( ComponentValue ) ;
OutColor . Component ( ChildIndex ) = ComponentValue ;
}
else
{
uint8 ComponentValue = 0 ;
ValueResult = SortedChildHandles [ ChildIndex ] - > GetValue ( ComponentValue ) ;
// Convert the FColor to a linear equivalent
OutColor . Component ( ChildIndex ) = ComponentValue / 255.0f ;
}
switch ( ValueResult )
{
case FPropertyAccess : : MultipleValues :
{
// Default the color to white if we've got multiple values selected
OutColor = FLinearColor : : White ;
return FPropertyAccess : : MultipleValues ;
}
case FPropertyAccess : : Fail :
return FPropertyAccess : : Fail ;
default :
break ;
}
}
// If we've got this far, we have to have successfully read a color with a single value
return FPropertyAccess : : Success ;
}
EVisibility FColorStructCustomization : : GetMultipleValuesTextVisibility ( ) const
{
FLinearColor Color ;
const FPropertyAccess : : Result ValueResult = GetColorAsLinear ( Color ) ;
return ( ValueResult = = FPropertyAccess : : MultipleValues ) ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
FReply FColorStructCustomization : : OnMouseButtonDownColorBlock ( const FGeometry & MyGeometry , const FPointerEvent & MouseEvent )
{
if ( MouseEvent . GetEffectingButton ( ) ! = EKeys : : LeftMouseButton )
{
return FReply : : Unhandled ( ) ;
}
2014-06-18 06:40:19 -04:00
CreateColorPicker ( /*bUseAlpha*/ true , bDontUpdateWhileEditing ) ;
2014-03-14 14:13:41 -04:00
//bIsInlineColorPickerVisible = !bIsInlineColorPickerVisible;
return FReply : : Handled ( ) ;
}
EVisibility FColorStructCustomization : : GetInlineColorPickerVisibility ( ) const
{
return bIsInlineColorPickerVisible ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
FReply FColorStructCustomization : : OnOpenFullColorPickerClicked ( )
{
2014-06-18 06:40:19 -04:00
CreateColorPicker ( /*bUseAlpha*/ true , bDontUpdateWhileEditing ) ;
2014-03-14 14:13:41 -04:00
bIsInlineColorPickerVisible = false ;
return FReply : : Handled ( ) ;
}
2014-04-23 17:45:47 -04:00
# undef LOCTEXT_NAMESPACE