Files
UnrealEngineUWP/Engine/Source/Editor/PropertyEditor/Private/ItemPropertyNode.cpp
nick darnell cdd3165da9 Editor - Giving the details panel the ability to filter the values as well (before we only filtered based on the name of properties and any custom filter strings coming from the customization. Now the values themselves are considered when filtering so that if you've referenced an asset or have some text property, you can search for specific text, or the asset name to see where it has been set.
#rb Matt.Kuhlenschmidt
[FYI] Matt.Kuhlenschmidt


#ROBOMERGE-OWNER: nick.darnell
#ROBOMERGE-AUTHOR: nick.darnell
#ROBOMERGE-SOURCE: CL 11358089 via CL 11358143 via CL 11358189
#ROBOMERGE-BOT: (v654-11333218)

[CL 11358651 by nick darnell in Main branch]
2020-02-11 16:46:12 -05:00

610 lines
19 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ItemPropertyNode.h"
#include "Misc/ConfigCacheIni.h"
#include "Classes/EditorStyleSettings.h"
#include "ObjectPropertyNode.h"
#include "PropertyEditorHelpers.h"
#define LOCTEXT_NAMESPACE "ItemPropertyNode"
FItemPropertyNode::FItemPropertyNode(void)
: FPropertyNode()
{
bCanDisplayFavorite = false;
}
FItemPropertyNode::~FItemPropertyNode(void)
{
}
uint8* FItemPropertyNode::GetValueBaseAddress(uint8* StartAddress, bool bIsSparseData) const
{
const FProperty* MyProperty = GetProperty();
if( MyProperty && ParentNodeWeakPtr.IsValid())
{
const FArrayProperty* OuterArrayProp = MyProperty->GetOwner<FArrayProperty>();
const FSetProperty* OuterSetProp = MyProperty->GetOwner<FSetProperty>();
const FMapProperty* OuterMapProp = MyProperty->GetOwner<FMapProperty>();
uint8* ValueBaseAddress = ParentNode->GetValueBaseAddress(StartAddress, bIsSparseData);
if (OuterArrayProp != nullptr)
{
FScriptArrayHelper ArrayHelper(OuterArrayProp, ValueBaseAddress);
if (ValueBaseAddress != nullptr && ArrayHelper.IsValidIndex(ArrayIndex))
{
return ArrayHelper.GetRawPtr(ArrayIndex);
}
}
else if (OuterSetProp != nullptr)
{
FScriptSetHelper SetHelper(OuterSetProp, ValueBaseAddress);
if (ValueBaseAddress != nullptr)
{
int32 ActualIndex = SetHelper.FindInternalIndex(ArrayIndex);
if (ActualIndex != INDEX_NONE)
{
return SetHelper.GetElementPtr(ActualIndex);
}
}
}
else if (OuterMapProp != nullptr)
{
FScriptMapHelper MapHelper(OuterMapProp, ValueBaseAddress);
if (ValueBaseAddress != nullptr)
{
int32 ActualIndex = MapHelper.FindInternalIndex(ArrayIndex);
if (ActualIndex != INDEX_NONE)
{
uint8* PairPtr = MapHelper.GetPairPtr(ActualIndex);
return MyProperty->ContainerPtrToValuePtr<uint8>(PairPtr);
}
}
}
else
{
uint8* ValueAddress = ParentNode->GetValueAddress(StartAddress, bIsSparseData);
if (ValueAddress != nullptr && ParentNode->GetProperty() != MyProperty)
{
// if this is not a fixed size array (in which the parent property and this property are the same), we need to offset from the property (otherwise, the parent already did that for us)
ValueAddress = Property->ContainerPtrToValuePtr<uint8>(ValueAddress);
}
if (ValueAddress != nullptr)
{
ValueAddress += ArrayOffset;
}
return ValueAddress;
}
}
return nullptr;
}
uint8* FItemPropertyNode::GetValueAddress(uint8* StartAddress, bool bIsSparseData) const
{
uint8* Result = GetValueBaseAddress(StartAddress, bIsSparseData);
const FProperty* MyProperty = GetProperty();
const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(MyProperty);
const FSetProperty* SetProperty = CastField<FSetProperty>(MyProperty);
const FMapProperty* MapProperty = CastField<FMapProperty>(MyProperty);
if( Result && ArrayProperty)
{
FScriptArrayHelper ArrayHelper(ArrayProperty, Result);
Result = ArrayHelper.GetRawPtr();
}
else if (Result && SetProperty)
{
FScriptSetHelper SetHelper(SetProperty, Result);
Result = SetHelper.GetElementPtr(0);
}
else if (Result && MapProperty)
{
FScriptMapHelper MapHelper(MapProperty, Result);
Result = MapHelper.GetPairPtr(0);
}
return Result;
}
/**
* Overridden function for special setup
*/
void FItemPropertyNode::InitExpansionFlags (void)
{
FProperty* MyProperty = GetProperty();
FReadAddressList Addresses;
bool bExpandableType = CastField<FStructProperty>(MyProperty)
|| ( ( CastField<FArrayProperty>(MyProperty) || CastField<FSetProperty>(MyProperty) || CastField<FMapProperty>(MyProperty) ) && GetReadAddress(false,Addresses) );
if( bExpandableType
|| HasNodeFlags(EPropertyNodeFlags::EditInlineNew)
|| HasNodeFlags(EPropertyNodeFlags::ShowInnerObjectProperties)
|| ( MyProperty->ArrayDim > 1 && ArrayIndex == -1 ) )
{
SetNodeFlags(EPropertyNodeFlags::CanBeExpanded, true);
}
}
/**
* Overridden function for Creating Child Nodes
*/
void FItemPropertyNode::InitChildNodes()
{
//NOTE - this is only turned off as to not invalidate child object nodes.
FProperty* MyProperty = GetProperty();
FStructProperty* StructProperty = CastField<FStructProperty>(MyProperty);
FArrayProperty* ArrayProperty = CastField<FArrayProperty>(MyProperty);
FSetProperty* SetProperty = CastField<FSetProperty>(MyProperty);
FMapProperty* MapProperty = CastField<FMapProperty>(MyProperty);
FObjectPropertyBase* ObjectProperty = CastField<FObjectPropertyBase>(MyProperty);
const bool bShouldShowHiddenProperties = !!HasNodeFlags(EPropertyNodeFlags::ShouldShowHiddenProperties);
const bool bShouldShowDisableEditOnInstance = !!HasNodeFlags(EPropertyNodeFlags::ShouldShowDisableEditOnInstance);
if( MyProperty->ArrayDim > 1 && ArrayIndex == -1 )
{
// Do not add array children which are defined by an enum but the enum at the array index is hidden
// This only applies to static arrays
static const FName NAME_ArraySizeEnum("ArraySizeEnum");
UEnum* ArraySizeEnum = NULL;
if (MyProperty->HasMetaData(NAME_ArraySizeEnum))
{
ArraySizeEnum = FindObject<UEnum>(NULL, *MyProperty->GetMetaData(NAME_ArraySizeEnum));
}
// Expand array.
for( int32 Index = 0 ; Index < MyProperty->ArrayDim ; Index++ )
{
bool bShouldBeHidden = false;
if( ArraySizeEnum )
{
// The enum at this array index is hidden
bShouldBeHidden = ArraySizeEnum->HasMetaData(TEXT("Hidden"), Index );
}
if( !bShouldBeHidden )
{
TSharedPtr<FItemPropertyNode> NewItemNode( new FItemPropertyNode);
FPropertyNodeInitParams InitParams;
InitParams.ParentNode = SharedThis(this);
InitParams.Property = MyProperty;
InitParams.ArrayOffset = Index*MyProperty->ElementSize;
InitParams.ArrayIndex = Index;
InitParams.bAllowChildren = true;
InitParams.bForceHiddenPropertyVisibility = bShouldShowHiddenProperties;
InitParams.bCreateDisableEditOnInstanceNodes = bShouldShowDisableEditOnInstance;
NewItemNode->InitNode( InitParams );
AddChildNode(NewItemNode);
}
}
}
else if( ArrayProperty )
{
void* Array = NULL;
FReadAddressList Addresses;
if ( GetReadAddress(!!HasNodeFlags(EPropertyNodeFlags::SingleSelectOnly), Addresses ) )
{
Array = Addresses.GetAddress(0);
}
if( Array )
{
FScriptArrayHelper ArrayHelper(ArrayProperty, Array);
for( int32 Index = 0 ; Index < ArrayHelper.Num(); Index++ )
{
TSharedPtr<FItemPropertyNode> NewItemNode( new FItemPropertyNode );
FPropertyNodeInitParams InitParams;
InitParams.ParentNode = SharedThis(this);
InitParams.Property = ArrayProperty->Inner;
InitParams.ArrayOffset = Index * ArrayProperty->Inner->ElementSize;
InitParams.ArrayIndex = Index;
InitParams.bAllowChildren = true;
InitParams.bForceHiddenPropertyVisibility = bShouldShowHiddenProperties;
InitParams.bCreateDisableEditOnInstanceNodes = bShouldShowDisableEditOnInstance;
NewItemNode->InitNode( InitParams );
AddChildNode(NewItemNode);
}
}
}
else if ( SetProperty )
{
void* Set = NULL;
FReadAddressList Addresses;
if (GetReadAddress(!!HasNodeFlags(EPropertyNodeFlags::SingleSelectOnly), Addresses))
{
Set = Addresses.GetAddress(0);
}
if (Set)
{
FScriptSetHelper SetHelper(SetProperty, Set);
for (int32 Index = 0; Index < SetHelper.Num(); ++Index)
{
TSharedPtr<FItemPropertyNode> NewItemNode(new FItemPropertyNode);
FPropertyNodeInitParams InitParams;
InitParams.ParentNode = SharedThis(this);
InitParams.Property = SetProperty->ElementProp;
InitParams.ArrayIndex = Index;
InitParams.bAllowChildren = true;
InitParams.bForceHiddenPropertyVisibility = bShouldShowHiddenProperties;
InitParams.bCreateDisableEditOnInstanceNodes = bShouldShowDisableEditOnInstance;
NewItemNode->InitNode(InitParams);
AddChildNode(NewItemNode);
}
}
}
else if ( MapProperty )
{
void* Map = NULL;
FReadAddressList Addresses;
if (GetReadAddress(!!HasNodeFlags(EPropertyNodeFlags::SingleSelectOnly), Addresses))
{
Map = Addresses.GetAddress(0);
}
if ( Map )
{
FScriptMapHelper MapHelper(MapProperty, Map);
for (int32 Index = 0; Index < MapHelper.Num(); ++Index)
{
// Construct the key node first
TSharedPtr<FPropertyNode> KeyNode(new FItemPropertyNode());
FPropertyNodeInitParams InitParams;
InitParams.ParentNode = SharedThis(this); // Key Node needs to point to this node to ensure its data is set correctly
InitParams.Property = MapHelper.KeyProp;
InitParams.ArrayIndex = Index;
InitParams.ArrayOffset = 0;
InitParams.bAllowChildren = true;
InitParams.bForceHiddenPropertyVisibility = bShouldShowHiddenProperties;
InitParams.bCreateDisableEditOnInstanceNodes = bShouldShowDisableEditOnInstance;
KeyNode->InitNode(InitParams);
// Not adding the key node as a child, otherwise it'll show up in the wrong spot.
// Then the value node
TSharedPtr<FPropertyNode> ValueNode(new FItemPropertyNode());
// Reuse the init params
InitParams.ParentNode = SharedThis(this);
InitParams.Property = MapHelper.ValueProp;
ValueNode->InitNode(InitParams);
AddChildNode(ValueNode);
FPropertyNode::SetupKeyValueNodePair(KeyNode, ValueNode);
}
}
}
else if( StructProperty )
{
// Expand struct.
TArray<FProperty*> StructMembers;
for (TFieldIterator<FProperty> It(StructProperty->Struct); It; ++It)
{
FProperty* StructMember = *It;
if (PropertyEditorHelpers::ShouldBeVisible(*this, StructMember))
{
StructMembers.Add(StructMember);
}
}
PropertyEditorHelpers::OrderPropertiesFromMetadata(StructMembers);
for (FProperty* StructMember : StructMembers)
{
TSharedPtr<FItemPropertyNode> NewItemNode( new FItemPropertyNode );
FPropertyNodeInitParams InitParams;
InitParams.ParentNode = SharedThis(this);
InitParams.Property = StructMember;
InitParams.ArrayOffset = 0;
InitParams.ArrayIndex = INDEX_NONE;
InitParams.bAllowChildren = true;
InitParams.bForceHiddenPropertyVisibility = bShouldShowHiddenProperties;
InitParams.bCreateDisableEditOnInstanceNodes = bShouldShowDisableEditOnInstance;
NewItemNode->InitNode( InitParams );
AddChildNode(NewItemNode);
if ( FPropertySettings::Get().ExpandDistributions() == false)
{
// auto-expand distribution structs
if ( CastField<FObjectProperty>(StructMember) || CastField<FWeakObjectProperty>(StructMember) || CastField<FLazyObjectProperty>(StructMember) || CastField<FSoftObjectProperty>(StructMember) )
{
const FName StructName = StructProperty->Struct->GetFName();
if (StructName == NAME_RawDistributionFloat || StructName == NAME_RawDistributionVector)
{
NewItemNode->SetNodeFlags(EPropertyNodeFlags::Expanded, true);
}
}
}
}
}
else if( ObjectProperty )
{
uint8* ReadValue = NULL;
FReadAddressList ReadAddresses;
if( GetReadAddress(!!HasNodeFlags(EPropertyNodeFlags::SingleSelectOnly), ReadAddresses, false ) )
{
// We've got some addresses, and we know they're all NULL or non-NULL.
// Have a peek at the first one, and only build an objects node if we've got addresses.
if( UObject* Obj = (ReadAddresses.Num() > 0) ? ObjectProperty->GetObjectPropertyValue(ReadAddresses.GetAddress(0)) : nullptr )
{
//verify it's not above in the hierarchy somewhere
FObjectPropertyNode* ParentObjectNode = FindObjectItemParent();
while (ParentObjectNode)
{
for ( TPropObjectIterator Itor( ParentObjectNode->ObjectIterator() ) ; Itor ; ++Itor )
{
if (*Itor == Obj)
{
SetNodeFlags(EPropertyNodeFlags::NoChildrenDueToCircularReference, true);
//stop the circular loop!!!
return;
}
}
FPropertyNode* UpwardTravesalNode = ParentObjectNode->GetParentNode();
ParentObjectNode = (UpwardTravesalNode==NULL) ? NULL : UpwardTravesalNode->FindObjectItemParent();
}
TSharedPtr<FObjectPropertyNode> NewObjectNode( new FObjectPropertyNode );
for ( int32 AddressIndex = 0 ; AddressIndex < ReadAddresses.Num() ; ++AddressIndex )
{
NewObjectNode->AddObject( ObjectProperty->GetObjectPropertyValue(ReadAddresses.GetAddress(AddressIndex) ) );
}
FPropertyNodeInitParams InitParams;
InitParams.ParentNode = SharedThis(this);
InitParams.Property = MyProperty;
InitParams.ArrayOffset = 0;
InitParams.ArrayIndex = INDEX_NONE;
InitParams.bAllowChildren = true;
InitParams.bForceHiddenPropertyVisibility = bShouldShowHiddenProperties;
InitParams.bCreateDisableEditOnInstanceNodes = bShouldShowDisableEditOnInstance;
NewObjectNode->InitNode( InitParams );
AddChildNode(NewObjectNode);
}
}
}
}
void FItemPropertyNode::SetFavorite(bool FavoriteValue)
{
const FObjectPropertyNode* CurrentObjectNode = FindObjectItemParent();
if (CurrentObjectNode == nullptr || CurrentObjectNode->GetNumObjects() <= 0)
return;
const UClass *ObjectClass = CurrentObjectNode->GetObjectBaseClass();
if (ObjectClass == nullptr)
return;
FString FullPropertyPath = ObjectClass->GetName() + TEXT(":") + PropertyPath;
if (FavoriteValue)
{
GConfig->SetBool(TEXT("DetailPropertyFavorites"), *FullPropertyPath, FavoriteValue, GEditorPerProjectIni);
}
else
{
GConfig->RemoveKey(TEXT("DetailPropertyFavorites"), *FullPropertyPath, GEditorPerProjectIni);
}
}
bool FItemPropertyNode::IsFavorite() const
{
const FObjectPropertyNode* CurrentObjectNode = FindObjectItemParent();
if (CurrentObjectNode == nullptr ||CurrentObjectNode->GetNumObjects() <= 0)
return false;
const UClass *ObjectClass = CurrentObjectNode->GetObjectBaseClass();
if (ObjectClass == nullptr)
return false;
FString FullPropertyPath = ObjectClass->GetName() + TEXT(":") + PropertyPath;
bool FavoritesPropertyValue = false;
if (!GConfig->GetBool(TEXT("DetailPropertyFavorites"), *FullPropertyPath, FavoritesPropertyValue, GEditorPerProjectIni))
return false;
return FavoritesPropertyValue;
}
/**
* Set the permission to display the favorite icon
*/
void FItemPropertyNode::SetCanDisplayFavorite(bool CanDisplayFavoriteIcon)
{
bCanDisplayFavorite = CanDisplayFavoriteIcon;
}
/**
* Set the permission to display the favorite icon
*/
bool FItemPropertyNode::CanDisplayFavorite() const
{
return bCanDisplayFavorite;
}
void FItemPropertyNode::SetDisplayNameOverride( const FText& InDisplayNameOverride )
{
DisplayNameOverride = InDisplayNameOverride;
}
FText FItemPropertyNode::GetDisplayName() const
{
FText FinalDisplayName;
if( !DisplayNameOverride.IsEmpty() )
{
FinalDisplayName = DisplayNameOverride;
}
else
{
const FProperty* PropertyPtr = GetProperty();
if( GetArrayIndex()==-1 && PropertyPtr != NULL )
{
// This item is not a member of an array, get a traditional display name
if ( FPropertySettings::Get().ShowFriendlyPropertyNames() )
{
//We are in "readable display name mode"../ Make a nice name
FinalDisplayName = PropertyPtr->GetDisplayNameText();
if ( FinalDisplayName.IsEmpty() )
{
FString PropertyDisplayName;
bool bIsBoolProperty = CastField<const FBoolProperty>(PropertyPtr) != NULL;
const FStructProperty* ParentStructProperty = CastField<const FStructProperty>(ParentNode->GetProperty());
if( ParentStructProperty && ParentStructProperty->Struct->GetFName() == NAME_Rotator )
{
if( Property->GetFName() == "Roll" )
{
PropertyDisplayName = TEXT("X");
}
else if( Property->GetFName() == "Pitch" )
{
PropertyDisplayName = TEXT("Y");
}
else if( Property->GetFName() == "Yaw" )
{
PropertyDisplayName = TEXT("Z");
}
else
{
check(0);
}
}
else
{
PropertyDisplayName = Property->GetName();
}
if( GetDefault<UEditorStyleSettings>()->bShowFriendlyNames )
{
PropertyDisplayName = FName::NameToDisplayString( PropertyDisplayName, bIsBoolProperty );
}
FinalDisplayName = FText::FromString( PropertyDisplayName );
}
}
else
{
FinalDisplayName = FText::FromString( PropertyPtr->GetName() );
}
}
else
{
// Get the ArraySizeEnum class from meta data.
static const FName NAME_ArraySizeEnum("ArraySizeEnum");
UEnum* ArraySizeEnum = NULL;
if (PropertyPtr && PropertyPtr->HasMetaData(NAME_ArraySizeEnum))
{
ArraySizeEnum = FindObject<UEnum>(NULL, *Property->GetMetaData(NAME_ArraySizeEnum));
}
// Sets and maps do not have a display index.
FProperty* ParentProperty = ParentNode->GetProperty();
// Also handle UArray's having the ArraySizeEnum entry...
if (ArraySizeEnum == nullptr && CastField<FArrayProperty>(ParentProperty) != nullptr && ParentProperty->HasMetaData(NAME_ArraySizeEnum))
{
ArraySizeEnum = FindObject<UEnum>(NULL, *ParentProperty->GetMetaData(NAME_ArraySizeEnum));
}
if (CastField<FSetProperty>(ParentProperty) == nullptr && CastField<FMapProperty>(ParentProperty) == nullptr)
{
// This item is a member of an array, its display name is its index
if (PropertyPtr == NULL || ArraySizeEnum == NULL)
{
FinalDisplayName = FText::AsNumber(GetArrayIndex());
}
else
{
FinalDisplayName = ArraySizeEnum->GetDisplayNameTextByIndex(GetArrayIndex());
}
}
// Maps should have display names that reflect the key and value types
else if (PropertyPtr != nullptr && CastField<FMapProperty>(ParentNode->GetProperty()) != nullptr)
{
FText FormatText = GetPropertyKeyNode().IsValid()
? LOCTEXT("MapValueDisplayFormat", "Value ({0})")
: LOCTEXT("MapKeyDisplayFormat", "Key ({0})");
FString TypeName;
if (const FStructProperty* StructProp = CastField<FStructProperty>(PropertyPtr))
{
// For struct props, use the name of the struct itself
TypeName = StructProp->Struct->GetName();
}
else if (const FEnumProperty* EnumProp = CastField<FEnumProperty>(PropertyPtr))
{
// For enum props, use the name of the enum
if (EnumProp->GetEnum() != nullptr)
{
TypeName = EnumProp->GetEnum()->GetName();
}
else
{
TypeName = TEXT("Enum");
}
}
else if(PropertyPtr->IsA<FStrProperty>())
{
// For strings, actually return "String" and not "Str"
TypeName = TEXT("String");
}
else
{
// For any other property, get the type from the property class
TypeName = PropertyPtr->GetClass()->GetName();
int32 EndIndex = TypeName.Find(TEXT("Property"), ESearchCase::IgnoreCase, ESearchDir::FromEnd);
if (EndIndex != -1)
{
TypeName.MidInline(0, EndIndex, false);
}
}
if (FPropertySettings::Get().ShowFriendlyPropertyNames())
{
TypeName = FName::NameToDisplayString(TypeName, false);
}
FinalDisplayName = FText::Format(FormatText, FText::FromString(TypeName));
}
}
}
return FinalDisplayName;
}
void FItemPropertyNode::SetToolTipOverride( const FText& InToolTipOverride )
{
ToolTipOverride = InToolTipOverride;
}
FText FItemPropertyNode::GetToolTipText() const
{
if(!ToolTipOverride.IsEmpty())
{
return ToolTipOverride;
}
return PropertyEditorHelpers::GetToolTipText(GetProperty());
}
#undef LOCTEXT_NAMESPACE