2019-12-26 15:33:43 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-06-04 05:48:07 -04:00
# include "EditConditionContext.h"
# include "EditConditionParser.h"
# include "PropertyNode.h"
# include "PropertyEditorHelpers.h"
2021-12-07 07:31:28 -05:00
DEFINE_LOG_CATEGORY ( LogEditCondition ) ;
2019-06-04 05:48:07 -04:00
FEditConditionContext : : FEditConditionContext ( FPropertyNode & InPropertyNode )
{
PropertyNode = InPropertyNode . AsShared ( ) ;
}
2020-01-07 15:54:23 -05:00
const FBoolProperty * FEditConditionContext : : GetSingleBoolProperty ( const TSharedPtr < FEditConditionExpression > & Expression ) const
2019-06-04 05:48:07 -04:00
{
if ( ! PropertyNode . IsValid ( ) )
{
return nullptr ;
}
2020-01-07 15:54:23 -05:00
const FProperty * Property = PropertyNode . Pin ( ) - > GetProperty ( ) ;
2019-06-04 05:48:07 -04:00
2020-01-07 15:54:23 -05:00
const FBoolProperty * BoolProperty = nullptr ;
2019-06-04 05:48:07 -04:00
for ( const FCompiledToken & Token : Expression - > Tokens )
{
if ( const EditConditionParserTokens : : FPropertyToken * PropertyToken = Token . Node . Cast < EditConditionParserTokens : : FPropertyToken > ( ) )
{
if ( BoolProperty ! = nullptr )
{
2021-09-06 08:35:51 -04:00
// second property token in the same expression, this can't be a simple expression like "bValue == false"
2019-06-04 05:48:07 -04:00
return nullptr ;
}
2021-09-06 08:35:51 -04:00
BoolProperty = FindFProperty < FBoolProperty > ( Property - > GetOwnerStruct ( ) , * PropertyToken - > PropertyName ) ;
2019-06-04 05:48:07 -04:00
if ( BoolProperty = = nullptr )
{
return nullptr ;
}
}
}
return BoolProperty ;
}
2021-09-06 08:35:51 -04:00
static TSet < TPair < FName , FString > > AlreadyLogged ;
2019-06-10 11:46:46 -04:00
2019-06-04 05:48:07 -04:00
template < typename T >
2021-09-06 08:35:51 -04:00
const T * FindTypedField ( const TSharedPtr < FPropertyNode > & PropertyNode , const FString & FieldName )
2019-06-04 05:48:07 -04:00
{
2021-09-06 08:35:51 -04:00
if ( ! PropertyNode . IsValid ( ) )
2019-06-04 05:48:07 -04:00
{
2021-09-06 08:35:51 -04:00
return nullptr ;
2019-06-04 05:48:07 -04:00
}
2021-09-06 08:35:51 -04:00
const FProperty * Property = PropertyNode - > GetProperty ( ) ;
if ( Property = = nullptr )
{
return nullptr ;
}
const FProperty * Field = FindFProperty < FProperty > ( Property - > GetOwnerStruct ( ) , * FieldName ) ;
if ( Field = = nullptr )
{
TPair < FName , FString > FieldKey ( Property - > GetOwnerStruct ( ) - > GetFName ( ) , FieldName ) ;
if ( ! AlreadyLogged . Find ( FieldKey ) )
{
AlreadyLogged . Add ( FieldKey ) ;
UE_LOG ( LogEditCondition , Error , TEXT ( " EditCondition parsing failed: Field name \" %s \" was not found in class \" %s \" . " ) , * FieldName , * Property - > GetOwnerStruct ( ) - > GetName ( ) ) ;
}
return nullptr ;
}
return CastField < T > ( Field ) ;
2019-06-04 05:48:07 -04:00
}
2019-10-15 04:32:11 -04:00
/**
* Get the parent to use as the context when evaluating the edit condition .
* For normal properties inside a UObject , this is the UObject .
* For children of containers , this is the UObject the container is in .
* Note : We do not support nested containers .
* The result can be nullptr in exceptional cases , eg . if the UI is getting rebuilt .
*/
2021-09-06 08:35:51 -04:00
static const FPropertyNode * GetEditConditionParentNode ( const TSharedPtr < FPropertyNode > & PropertyNode )
2019-09-18 10:35:25 -04:00
{
2021-09-06 08:35:51 -04:00
const FPropertyNode * ParentNode = PropertyNode - > GetParentNode ( ) ;
2020-01-07 15:54:23 -05:00
FFieldVariant PropertyOuter = PropertyNode - > GetProperty ( ) - > GetOwnerVariant ( ) ;
2019-09-18 10:35:25 -04:00
2020-01-07 15:54:23 -05:00
if ( PropertyOuter . Get < FArrayProperty > ( ) ! = nullptr | |
PropertyOuter . Get < FSetProperty > ( ) ! = nullptr | |
PropertyOuter . Get < FMapProperty > ( ) ! = nullptr )
2019-09-18 10:35:25 -04:00
{
// in a dynamic container, parent is actually one level up
return ParentNode - > GetParentNode ( ) ;
}
2020-08-11 01:36:57 -04:00
if ( PropertyNode - > GetProperty ( ) - > ArrayDim > 1 & & PropertyNode - > GetArrayIndex ( ) ! = INDEX_NONE )
{
// in a fixed size container, parent node is just the header field
return ParentNode - > GetParentNode ( ) ;
}
2019-09-18 10:35:25 -04:00
return ParentNode ;
}
2021-09-06 08:35:51 -04:00
static const uint8 * GetPropertyValuePtr ( const FProperty * Property , const TSharedPtr < FPropertyNode > & PropertyNode , const FPropertyNode * ParentNode , const FComplexPropertyNode * ComplexParentNode , int32 Index )
2019-10-15 03:46:06 -04:00
{
2021-09-06 08:35:51 -04:00
const uint8 * ValuePtr = ComplexParentNode - > GetValuePtrOfInstance ( Index , Property , ParentNode ) ;
2019-10-15 03:46:06 -04:00
return ValuePtr ;
}
2019-06-04 05:48:07 -04:00
TOptional < bool > FEditConditionContext : : GetBoolValue ( const FString & PropertyName ) const
{
TSharedPtr < FPropertyNode > PinnedNode = PropertyNode . Pin ( ) ;
2019-10-15 04:32:11 -04:00
if ( ! PinnedNode . IsValid ( ) )
{
return TOptional < bool > ( ) ;
}
2021-09-06 08:35:51 -04:00
const FBoolProperty * BoolProperty = FindTypedField < FBoolProperty > ( PinnedNode , PropertyName ) ;
if ( BoolProperty = = nullptr )
{
return TOptional < bool > ( ) ;
}
const FPropertyNode * ParentNode = GetEditConditionParentNode ( PinnedNode ) ;
2019-10-15 04:32:11 -04:00
if ( ParentNode = = nullptr )
{
return TOptional < bool > ( ) ;
}
2021-09-06 08:35:51 -04:00
const FComplexPropertyNode * ComplexParentNode = PinnedNode - > FindComplexParent ( ) ;
2020-09-01 14:07:48 -04:00
if ( ComplexParentNode = = nullptr )
{
return TOptional < bool > ( ) ;
}
2019-09-18 10:35:25 -04:00
TOptional < bool > Result ;
2019-06-04 05:48:07 -04:00
for ( int32 Index = 0 ; Index < ComplexParentNode - > GetInstancesNum ( ) ; + + Index )
{
2021-09-06 08:35:51 -04:00
const uint8 * ValuePtr = GetPropertyValuePtr ( BoolProperty , PinnedNode , ParentNode , ComplexParentNode , Index ) ;
2019-10-15 03:46:06 -04:00
if ( ValuePtr = = nullptr )
2019-06-13 10:10:27 -04:00
{
return TOptional < bool > ( ) ;
}
2019-06-04 05:48:07 -04:00
bool bValue = BoolProperty - > GetPropertyValue ( ValuePtr ) ;
if ( ! Result . IsSet ( ) )
{
Result = bValue ;
}
else if ( Result . GetValue ( ) ! = bValue )
{
// all values aren't the same...
return TOptional < bool > ( ) ;
}
}
return Result ;
}
2019-10-15 03:46:06 -04:00
TOptional < int64 > FEditConditionContext : : GetIntegerValue ( const FString & PropertyName ) const
{
TSharedPtr < FPropertyNode > PinnedNode = PropertyNode . Pin ( ) ;
2019-10-15 04:32:11 -04:00
if ( ! PinnedNode . IsValid ( ) )
{
return TOptional < int64 > ( ) ;
}
2021-09-06 08:35:51 -04:00
const FNumericProperty * NumericProperty = FindTypedField < FNumericProperty > ( PinnedNode , PropertyName ) ;
if ( NumericProperty = = nullptr | | ! NumericProperty - > IsInteger ( ) )
{
return TOptional < int64 > ( ) ;
}
const FPropertyNode * ParentNode = GetEditConditionParentNode ( PinnedNode ) ;
2019-10-15 04:32:11 -04:00
if ( ParentNode = = nullptr )
{
return TOptional < int64 > ( ) ;
}
2021-09-06 08:35:51 -04:00
const FComplexPropertyNode * ComplexParentNode = PinnedNode - > FindComplexParent ( ) ;
2020-09-01 14:07:48 -04:00
if ( ComplexParentNode = = nullptr )
{
return TOptional < int64 > ( ) ;
}
2019-10-15 03:46:06 -04:00
TOptional < int64 > Result ;
for ( int32 Index = 0 ; Index < ComplexParentNode - > GetInstancesNum ( ) ; + + Index )
{
2021-09-06 08:35:51 -04:00
const uint8 * ValuePtr = GetPropertyValuePtr ( NumericProperty , PinnedNode , ParentNode , ComplexParentNode , Index ) ;
2019-10-15 03:46:06 -04:00
if ( ValuePtr = = nullptr )
{
return TOptional < int64 > ( ) ;
}
int64 Value = NumericProperty - > GetSignedIntPropertyValue ( ValuePtr ) ;
if ( ! Result . IsSet ( ) )
{
Result = Value ;
}
else if ( Result . GetValue ( ) ! = Value )
{
// all values aren't the same...
return TOptional < int64 > ( ) ;
}
}
return Result ;
}
2019-06-04 05:48:07 -04:00
TOptional < double > FEditConditionContext : : GetNumericValue ( const FString & PropertyName ) const
{
TSharedPtr < FPropertyNode > PinnedNode = PropertyNode . Pin ( ) ;
2019-10-15 04:32:11 -04:00
if ( ! PinnedNode . IsValid ( ) )
{
return TOptional < double > ( ) ;
}
2021-09-06 08:35:51 -04:00
const FNumericProperty * NumericProperty = FindTypedField < FNumericProperty > ( PinnedNode , PropertyName ) ;
if ( NumericProperty = = nullptr )
{
return TOptional < double > ( ) ;
}
const FPropertyNode * ParentNode = GetEditConditionParentNode ( PinnedNode ) ;
2019-10-15 04:32:11 -04:00
if ( ParentNode = = nullptr )
{
return TOptional < double > ( ) ;
}
2021-09-06 08:35:51 -04:00
const FComplexPropertyNode * ComplexParentNode = PinnedNode - > FindComplexParent ( ) ;
2020-09-01 14:07:48 -04:00
if ( ComplexParentNode = = nullptr )
{
return TOptional < double > ( ) ;
}
2019-06-04 05:48:07 -04:00
TOptional < double > Result ;
for ( int32 Index = 0 ; Index < ComplexParentNode - > GetInstancesNum ( ) ; + + Index )
{
2021-09-06 08:35:51 -04:00
const uint8 * ValuePtr = GetPropertyValuePtr ( NumericProperty , PinnedNode , ParentNode , ComplexParentNode , Index ) ;
2019-10-15 03:46:06 -04:00
if ( ValuePtr = = nullptr )
2019-06-13 10:10:27 -04:00
{
return TOptional < double > ( ) ;
}
2019-06-04 05:48:07 -04:00
double Value = 0 ;
if ( NumericProperty - > IsInteger ( ) )
{
Value = ( double ) NumericProperty - > GetSignedIntPropertyValue ( ValuePtr ) ;
}
else if ( NumericProperty - > IsFloatingPoint ( ) )
{
Value = NumericProperty - > GetFloatingPointPropertyValue ( ValuePtr ) ;
}
if ( ! Result . IsSet ( ) )
{
Result = Value ;
}
else if ( ! FMath : : IsNearlyEqual ( Result . GetValue ( ) , Value ) )
{
// all values aren't the same...
return TOptional < double > ( ) ;
}
}
return Result ;
}
TOptional < FString > FEditConditionContext : : GetEnumValue ( const FString & PropertyName ) const
{
2021-09-06 08:35:51 -04:00
TSharedPtr < FPropertyNode > PinnedNode = PropertyNode . Pin ( ) ;
if ( ! PinnedNode . IsValid ( ) )
{
return TOptional < FString > ( ) ;
}
const FProperty * Property = FindTypedField < FProperty > ( PinnedNode , PropertyName ) ;
2019-06-04 05:48:07 -04:00
if ( Property = = nullptr )
{
return TOptional < FString > ( ) ;
}
const UEnum * EnumType = nullptr ;
2020-01-07 15:54:23 -05:00
const FNumericProperty * NumericProperty = nullptr ;
if ( const FEnumProperty * EnumProperty = CastField < FEnumProperty > ( Property ) )
2019-06-04 05:48:07 -04:00
{
NumericProperty = EnumProperty - > GetUnderlyingProperty ( ) ;
EnumType = EnumProperty - > GetEnum ( ) ;
}
2020-01-07 15:54:23 -05:00
else if ( const FByteProperty * ByteProperty = CastField < FByteProperty > ( Property ) )
2019-06-04 05:48:07 -04:00
{
NumericProperty = ByteProperty ;
EnumType = ByteProperty - > GetIntPropertyEnum ( ) ;
}
2021-09-06 08:35:51 -04:00
2021-09-10 11:10:30 -04:00
if ( EnumType = = nullptr | | NumericProperty = = nullptr | | ! NumericProperty - > IsInteger ( ) )
2019-06-04 05:48:07 -04:00
{
return TOptional < FString > ( ) ;
}
2019-10-15 03:46:06 -04:00
2021-09-06 08:35:51 -04:00
const FPropertyNode * ParentNode = GetEditConditionParentNode ( PinnedNode ) ;
2019-10-15 04:32:11 -04:00
if ( ParentNode = = nullptr )
{
return TOptional < FString > ( ) ;
}
2021-09-06 08:35:51 -04:00
const FComplexPropertyNode * ComplexParentNode = PinnedNode - > FindComplexParent ( ) ;
2020-09-01 14:07:48 -04:00
if ( ComplexParentNode = = nullptr )
{
return TOptional < FString > ( ) ;
}
2019-09-18 10:35:25 -04:00
TOptional < int64 > Result ;
2019-06-04 05:48:07 -04:00
for ( int32 Index = 0 ; Index < ComplexParentNode - > GetInstancesNum ( ) ; + + Index )
{
2021-09-15 03:14:53 -04:00
// NOTE: this very intentionally fetches the value from Property, not NumericProperty,
// because the underlying property of an enum does not return a valid value
2021-09-10 11:10:30 -04:00
const uint8 * ValuePtr = GetPropertyValuePtr ( Property , PinnedNode , ParentNode , ComplexParentNode , Index ) ;
2019-10-15 03:46:06 -04:00
if ( ValuePtr = = nullptr )
2019-06-13 10:10:27 -04:00
{
return TOptional < FString > ( ) ;
}
2021-09-10 11:10:30 -04:00
const int64 Value = NumericProperty - > GetSignedIntPropertyValue ( ValuePtr ) ;
2019-06-04 05:48:07 -04:00
if ( ! Result . IsSet ( ) )
{
Result = Value ;
}
else if ( Result . GetValue ( ) ! = Value )
{
// all values aren't the same...
return TOptional < FString > ( ) ;
}
}
2019-10-15 03:46:06 -04:00
if ( ! Result . IsSet ( ) )
2019-06-04 05:48:07 -04:00
{
2019-10-15 03:46:06 -04:00
return TOptional < FString > ( ) ;
2019-06-04 05:48:07 -04:00
}
2019-10-15 03:46:06 -04:00
return EnumType - > GetNameStringByValue ( Result . GetValue ( ) ) ;
2019-06-04 05:48:07 -04:00
}
2019-10-10 10:08:16 -04:00
TOptional < UObject * > FEditConditionContext : : GetPointerValue ( const FString & PropertyName ) const
{
2021-09-06 08:35:51 -04:00
TSharedPtr < FPropertyNode > PinnedNode = PropertyNode . Pin ( ) ;
if ( ! PinnedNode . IsValid ( ) )
{
return TOptional < UObject * > ( ) ;
}
const FProperty * Property = FindTypedField < FProperty > ( PinnedNode , PropertyName ) ;
2019-10-10 10:08:16 -04:00
if ( Property = = nullptr )
{
return TOptional < UObject * > ( ) ;
}
2020-01-07 15:54:23 -05:00
const FObjectPropertyBase * ObjectProperty = CastField < FObjectPropertyBase > ( Property ) ;
2019-10-10 10:08:16 -04:00
if ( ObjectProperty = = nullptr )
{
return TOptional < UObject * > ( ) ;
}
2021-09-06 08:35:51 -04:00
const FPropertyNode * ParentNode = GetEditConditionParentNode ( PinnedNode ) ;
2019-10-15 04:32:11 -04:00
if ( ParentNode = = nullptr )
{
return TOptional < UObject * > ( ) ;
}
2021-09-06 08:35:51 -04:00
const FComplexPropertyNode * ComplexParentNode = PinnedNode - > FindComplexParent ( ) ;
2020-09-01 14:07:48 -04:00
if ( ComplexParentNode = = nullptr )
{
return TOptional < UObject * > ( ) ;
}
2019-10-10 10:08:16 -04:00
TOptional < UObject * > Result ;
for ( int32 Index = 0 ; Index < ComplexParentNode - > GetInstancesNum ( ) ; + + Index )
{
2021-09-06 08:35:51 -04:00
const uint8 * ValuePtr = GetPropertyValuePtr ( Property , PinnedNode , ParentNode , ComplexParentNode , Index ) ;
2019-10-15 03:46:06 -04:00
if ( ValuePtr = = nullptr )
2019-10-10 10:08:16 -04:00
{
return TOptional < UObject * > ( ) ;
}
UObject * Value = ObjectProperty - > GetObjectPropertyValue ( ValuePtr ) ;
if ( ! Result . IsSet ( ) )
{
Result = Value ;
}
else if ( Result . GetValue ( ) ! = Value )
{
// all values aren't the same
return TOptional < UObject * > ( ) ;
}
}
return Result ;
}
2019-06-04 05:48:07 -04:00
TOptional < FString > FEditConditionContext : : GetTypeName ( const FString & PropertyName ) const
{
2021-09-06 08:35:51 -04:00
TSharedPtr < FPropertyNode > PinnedNode = PropertyNode . Pin ( ) ;
if ( ! PinnedNode . IsValid ( ) )
{
return TOptional < FString > ( ) ;
}
const FProperty * Property = FindTypedField < FProperty > ( PinnedNode , PropertyName ) ;
2019-06-04 05:48:07 -04:00
if ( Property = = nullptr )
{
return TOptional < FString > ( ) ;
}
2020-01-07 15:54:23 -05:00
if ( const FEnumProperty * EnumProperty = CastField < FEnumProperty > ( Property ) )
2019-06-04 05:48:07 -04:00
{
2021-09-10 11:10:30 -04:00
return EnumProperty - > GetEnum ( ) - > GetName ( ) ;
2019-06-04 05:48:07 -04:00
}
2020-01-07 15:54:23 -05:00
else if ( const FByteProperty * ByteProperty = CastField < FByteProperty > ( Property ) )
2019-06-04 05:48:07 -04:00
{
2021-09-06 08:35:51 -04:00
const UEnum * EnumType = ByteProperty - > GetIntPropertyEnum ( ) ;
if ( EnumType ! = nullptr )
{
return EnumType - > GetName ( ) ;
}
2019-06-04 05:48:07 -04:00
}
return Property - > GetCPPType ( ) ;
2019-10-15 03:46:06 -04:00
}
TOptional < int64 > FEditConditionContext : : GetIntegerValueOfEnum ( const FString & EnumTypeName , const FString & MemberName ) const
{
2021-09-06 08:35:51 -04:00
const UEnum * EnumType = FindObject < UEnum > ( ( UObject * ) ANY_PACKAGE , * EnumTypeName , true ) ;
2019-10-15 03:46:06 -04:00
if ( EnumType = = nullptr )
{
return TOptional < int64 > ( ) ;
}
2021-09-10 11:10:30 -04:00
const int64 EnumValue = EnumType - > GetValueByName ( FName ( * MemberName ) ) ;
2019-10-15 03:46:06 -04:00
if ( EnumValue = = INDEX_NONE )
{
return TOptional < int64 > ( ) ;
}
return EnumValue ;
}