Files
UnrealEngineUWP/Engine/Source/Editor/PropertyEditor/Private/PropertyEditorModule.cpp
jeanmichel dignard 2ce7666d2d Copying //UE4/Dev-Core [at] 10708550 to Dev-Main (//UE4/Dev-Main)
#rb none

#ROBOMERGE-OWNER: jeanmichel.dignard
#ROBOMERGE-AUTHOR: robert.manuszewski
#ROBOMERGE-SOURCE: CL 10708666 in //UE4/Main/...
#ROBOMERGE-BOT: TOOLS (Main -> Dev-Tools-Staging) (v626-10872990)

[CL 10898071 by jeanmichel dignard in Dev-Tools-Staging branch]
2020-01-07 15:54:23 -05:00

795 lines
25 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "PropertyEditorModule.h"
#include "UObject/UnrealType.h"
#include "Widgets/Layout/SBorder.h"
#include "Modules/ModuleManager.h"
#include "Framework/Application/SlateApplication.h"
#include "Engine/UserDefinedEnum.h"
#include "Engine/UserDefinedStruct.h"
#include "Presentation/PropertyEditor/PropertyEditor.h"
#include "SSingleProperty.h"
#include "IDetailsView.h"
#include "SDetailsView.h"
#include "IPropertyTableWidgetHandle.h"
#include "IPropertyTable.h"
#include "UserInterface/PropertyTable/SPropertyTable.h"
#include "UserInterface/PropertyTable/PropertyTableWidgetHandle.h"
#include "IAssetTools.h"
#include "AssetToolsModule.h"
#include "SPropertyTreeViewImpl.h"
#include "Interfaces/IMainFrameModule.h"
#include "IPropertyChangeListener.h"
#include "PropertyChangeListener.h"
#include "Toolkits/AssetEditorToolkit.h"
#include "PropertyEditorToolkit.h"
#include "Presentation/PropertyTable/PropertyTable.h"
#include "IPropertyTableCellPresenter.h"
#include "UserInterface/PropertyTable/TextPropertyTableCellPresenter.h"
#include "SStructureDetailsView.h"
#include "Widgets/Colors/SColorPicker.h"
#include "PropertyRowGenerator.h"
IMPLEMENT_MODULE( FPropertyEditorModule, PropertyEditor );
TSharedRef<IPropertyTypeCustomization> FPropertyTypeLayoutCallback::GetCustomizationInstance() const
{
return PropertyTypeLayoutDelegate.Execute();
}
void FPropertyTypeLayoutCallbackList::Add( const FPropertyTypeLayoutCallback& NewCallback )
{
if( !NewCallback.PropertyTypeIdentifier.IsValid() )
{
BaseCallback = NewCallback;
}
else
{
IdentifierList.Add( NewCallback );
}
}
void FPropertyTypeLayoutCallbackList::Remove( const TSharedPtr<IPropertyTypeIdentifier>& InIdentifier )
{
if( !InIdentifier.IsValid() )
{
BaseCallback = FPropertyTypeLayoutCallback();
}
else
{
IdentifierList.RemoveAllSwap( [&InIdentifier]( FPropertyTypeLayoutCallback& Callback) { return Callback.PropertyTypeIdentifier == InIdentifier; } );
}
}
const FPropertyTypeLayoutCallback& FPropertyTypeLayoutCallbackList::Find( const IPropertyHandle& PropertyHandle ) const
{
if( IdentifierList.Num() > 0 )
{
const FPropertyTypeLayoutCallback* Callback =
IdentifierList.FindByPredicate
(
[&]( const FPropertyTypeLayoutCallback& InCallback )
{
return InCallback.PropertyTypeIdentifier->IsPropertyTypeCustomized( PropertyHandle );
}
);
if( Callback )
{
return *Callback;
}
}
return BaseCallback;
}
void FPropertyEditorModule::StartupModule()
{
}
void FPropertyEditorModule::ShutdownModule()
{
// NOTE: It's vital that we clean up everything created by this DLL here! We need to make sure there
// are no outstanding references to objects as the compiled code for this module's class will
// literally be unloaded from memory after this function exits. This even includes instantiated
// templates, such as delegate wrapper objects that are allocated by the module!
DestroyColorPicker();
AllDetailViews.Empty();
AllSinglePropertyViews.Empty();
}
void FPropertyEditorModule::NotifyCustomizationModuleChanged()
{
if (FSlateApplication::IsInitialized())
{
// The module was changed (loaded or unloaded), force a refresh. Note it is assumed the module unregisters all customization delegates before this
for (int32 ViewIndex = 0; ViewIndex < AllDetailViews.Num(); ++ViewIndex)
{
if (AllDetailViews[ViewIndex].IsValid())
{
TSharedPtr<SDetailsView> DetailViewPin = AllDetailViews[ViewIndex].Pin();
DetailViewPin->ForceRefresh();
}
}
}
}
static bool ShouldShowProperty(const FPropertyAndParent& PropertyAndParent, bool bHaveTemplate)
{
const FProperty& Property = PropertyAndParent.Property;
if ( bHaveTemplate )
{
const UClass* PropertyOwnerClass = Property.GetOwner<const UClass>();
const bool bDisableEditOnTemplate = PropertyOwnerClass
&& PropertyOwnerClass->IsNative()
&& Property.HasAnyPropertyFlags(CPF_DisableEditOnTemplate);
if(bDisableEditOnTemplate)
{
return false;
}
}
return true;
}
TSharedRef<SWindow> FPropertyEditorModule::CreateFloatingDetailsView( const TArray< UObject* >& InObjects, bool bIsLockable )
{
TSharedRef<SWindow> NewSlateWindow = SNew(SWindow)
.Title( NSLOCTEXT("PropertyEditor", "WindowTitle", "Property Editor") )
.ClientSize( FVector2D(400, 550) );
// If the main frame exists parent the window to it
TSharedPtr< SWindow > ParentWindow;
if( FModuleManager::Get().IsModuleLoaded( "MainFrame" ) )
{
IMainFrameModule& MainFrame = FModuleManager::GetModuleChecked<IMainFrameModule>( "MainFrame" );
ParentWindow = MainFrame.GetParentWindow();
}
if( ParentWindow.IsValid() )
{
// Parent the window to the main frame
FSlateApplication::Get().AddWindowAsNativeChild( NewSlateWindow, ParentWindow.ToSharedRef() );
}
else
{
FSlateApplication::Get().AddWindow( NewSlateWindow );
}
FDetailsViewArgs Args;
Args.bHideSelectionTip = true;
Args.bLockable = bIsLockable;
FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
TSharedRef<IDetailsView> DetailView = PropertyEditorModule.CreateDetailView( Args );
bool bHaveTemplate = false;
for(int32 i=0; i<InObjects.Num(); i++)
{
if(InObjects[i] != NULL && InObjects[i]->IsTemplate())
{
bHaveTemplate = true;
break;
}
}
DetailView->SetIsPropertyVisibleDelegate( FIsPropertyVisible::CreateStatic(&ShouldShowProperty, bHaveTemplate) );
DetailView->SetObjects( InObjects );
NewSlateWindow->SetContent(
SNew(SBorder)
.BorderImage( FEditorStyle::GetBrush(TEXT("PropertyWindow.WindowBorder")) )
[
DetailView
]
);
return NewSlateWindow;
}
TSharedRef<SPropertyTreeViewImpl> FPropertyEditorModule::CreatePropertyView(
UObject* InObject,
bool bAllowFavorites,
bool bIsLockable,
bool bHiddenPropertyVisibility,
bool bAllowSearch,
bool ShowTopLevelNodes,
FNotifyHook* InNotifyHook,
float InNameColumnWidth,
FOnPropertySelectionChanged OnPropertySelectionChanged,
FOnPropertyClicked OnPropertyMiddleClicked,
FConstructExternalColumnHeaders ConstructExternalColumnHeaders,
FConstructExternalColumnCell ConstructExternalColumnCell)
{
TSharedRef<SPropertyTreeViewImpl> PropertyView =
SNew( SPropertyTreeViewImpl )
.IsLockable( bIsLockable )
.AllowFavorites( bAllowFavorites )
.HiddenPropertyVis( bHiddenPropertyVisibility )
.NotifyHook( InNotifyHook )
.AllowSearch( bAllowSearch )
.ShowTopLevelNodes( ShowTopLevelNodes )
.NameColumnWidth( InNameColumnWidth )
.OnPropertySelectionChanged( OnPropertySelectionChanged )
.OnPropertyMiddleClicked( OnPropertyMiddleClicked )
.ConstructExternalColumnHeaders( ConstructExternalColumnHeaders )
.ConstructExternalColumnCell( ConstructExternalColumnCell );
if( InObject )
{
TArray< TWeakObjectPtr< UObject > > Objects;
Objects.Add( InObject );
PropertyView->SetObjectArray( Objects );
}
return PropertyView;
}
TSharedPtr<FAssetThumbnailPool> FPropertyEditorModule::GetThumbnailPool()
{
if (!GlobalThumbnailPool.IsValid())
{
// Create a thumbnail pool for the view if it doesn't exist. This does not use resources of no thumbnails are used
GlobalThumbnailPool = MakeShareable(new FAssetThumbnailPool(50, true));
}
return GlobalThumbnailPool;
}
TSharedRef<IDetailsView> FPropertyEditorModule::CreateDetailView( const FDetailsViewArgs& DetailsViewArgs )
{
// Compact the list of detail view instances
for( int32 ViewIndex = 0; ViewIndex < AllDetailViews.Num(); ++ViewIndex )
{
if ( !AllDetailViews[ViewIndex].IsValid() )
{
AllDetailViews.RemoveAtSwap( ViewIndex );
--ViewIndex;
}
}
TSharedRef<SDetailsView> DetailView =
SNew( SDetailsView )
.DetailsViewArgs( DetailsViewArgs );
AllDetailViews.Add( DetailView );
PropertyEditorOpened.Broadcast();
return DetailView;
}
TSharedPtr<IDetailsView> FPropertyEditorModule::FindDetailView( const FName ViewIdentifier ) const
{
if(ViewIdentifier.IsNone())
{
return nullptr;
}
for(auto It = AllDetailViews.CreateConstIterator(); It; ++It)
{
TSharedPtr<SDetailsView> DetailsView = It->Pin();
if(DetailsView.IsValid() && DetailsView->GetIdentifier() == ViewIdentifier)
{
return DetailsView;
}
}
return nullptr;
}
TSharedPtr<ISinglePropertyView> FPropertyEditorModule::CreateSingleProperty( UObject* InObject, FName InPropertyName, const FSinglePropertyParams& InitParams )
{
// Compact the list of detail view instances
for( int32 ViewIndex = 0; ViewIndex < AllSinglePropertyViews.Num(); ++ViewIndex )
{
if ( !AllSinglePropertyViews[ViewIndex].IsValid() )
{
AllSinglePropertyViews.RemoveAtSwap( ViewIndex );
--ViewIndex;
}
}
TSharedRef<SSingleProperty> Property =
SNew( SSingleProperty )
.Object( InObject )
.PropertyName( InPropertyName )
.NamePlacement( InitParams.NamePlacement )
.NameOverride( InitParams.NameOverride )
.NotifyHook( InitParams.NotifyHook )
.PropertyFont( InitParams.Font );
if( Property->HasValidProperty() )
{
AllSinglePropertyViews.Add( Property );
return Property;
}
return NULL;
}
TSharedRef< IPropertyTable > FPropertyEditorModule::CreatePropertyTable()
{
return MakeShareable( new FPropertyTable() );
}
TSharedRef< SWidget > FPropertyEditorModule::CreatePropertyTableWidget( const TSharedRef< class IPropertyTable >& PropertyTable )
{
return SNew( SPropertyTable, PropertyTable );
}
TSharedRef< SWidget > FPropertyEditorModule::CreatePropertyTableWidget( const TSharedRef< IPropertyTable >& PropertyTable, const TArray< TSharedRef< class IPropertyTableCustomColumn > >& Customizations )
{
return SNew( SPropertyTable, PropertyTable )
.ColumnCustomizations( Customizations );
}
TSharedRef< IPropertyTableWidgetHandle > FPropertyEditorModule::CreatePropertyTableWidgetHandle( const TSharedRef< IPropertyTable >& PropertyTable )
{
TSharedRef< FPropertyTableWidgetHandle > FWidgetHandle = MakeShareable( new FPropertyTableWidgetHandle( SNew( SPropertyTable, PropertyTable ) ) );
TSharedRef< IPropertyTableWidgetHandle > IWidgetHandle = StaticCastSharedRef<IPropertyTableWidgetHandle>(FWidgetHandle);
return IWidgetHandle;
}
TSharedRef< IPropertyTableWidgetHandle > FPropertyEditorModule::CreatePropertyTableWidgetHandle( const TSharedRef< IPropertyTable >& PropertyTable, const TArray< TSharedRef< class IPropertyTableCustomColumn > >& Customizations )
{
TSharedRef< FPropertyTableWidgetHandle > FWidgetHandle = MakeShareable( new FPropertyTableWidgetHandle( SNew( SPropertyTable, PropertyTable )
.ColumnCustomizations( Customizations )));
TSharedRef< IPropertyTableWidgetHandle > IWidgetHandle = StaticCastSharedRef<IPropertyTableWidgetHandle>(FWidgetHandle);
return IWidgetHandle;
}
TSharedRef< IPropertyTableCellPresenter > FPropertyEditorModule::CreateTextPropertyCellPresenter(const TSharedRef< class FPropertyNode >& InPropertyNode, const TSharedRef< class IPropertyTableUtilities >& InPropertyUtilities,
const FSlateFontInfo* InFontPtr /* = NULL */)
{
FSlateFontInfo InFont;
if (InFontPtr == NULL)
{
// Encapsulating reference to Private file PropertyTableConstants.h
InFont = FEditorStyle::GetFontStyle( PropertyTableConstants::NormalFontStyle );
}
else
{
InFont = *InFontPtr;
}
TSharedRef< FPropertyEditor > PropertyEditor = FPropertyEditor::Create( InPropertyNode, InPropertyUtilities );
return MakeShareable( new FTextPropertyTableCellPresenter( PropertyEditor, InPropertyUtilities, InFont) );
}
FStructProperty* FPropertyEditorModule::RegisterStructOnScopeProperty(TSharedRef<FStructOnScope> StructOnScope)
{
const FName StructName = StructOnScope->GetStruct()->GetFName();
FStructProperty* StructProperty = RegisteredStructToProxyMap.FindRef(StructName);
if(!StructProperty)
{
UScriptStruct* InnerStruct = Cast<UScriptStruct>(const_cast<UStruct*>(StructOnScope->GetStruct()));
StructProperty = new FStructProperty(InnerStruct, *MakeUniqueObjectName(InnerStruct, UField::StaticClass(), InnerStruct->GetFName()).ToString(), RF_NoFlags, 0, CPF_None, InnerStruct);
StructProperty->Struct = InnerStruct;
StructProperty->ElementSize = StructOnScope->GetStruct()->GetStructureSize();
RegisteredStructToProxyMap.Add(StructName, StructProperty);
}
return StructProperty;
}
TSharedRef< FAssetEditorToolkit > FPropertyEditorModule::CreatePropertyEditorToolkit( const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UObject* ObjectToEdit )
{
return FPropertyEditorToolkit::CreateEditor( Mode, InitToolkitHost, ObjectToEdit );
}
TSharedRef< FAssetEditorToolkit > FPropertyEditorModule::CreatePropertyEditorToolkit( const EToolkitMode::Type Mode, const TSharedPtr< IToolkitHost >& InitToolkitHost, const TArray< UObject* >& ObjectsToEdit )
{
return FPropertyEditorToolkit::CreateEditor( Mode, InitToolkitHost, ObjectsToEdit );
}
TSharedRef< FAssetEditorToolkit > FPropertyEditorModule::CreatePropertyEditorToolkit( const EToolkitMode::Type Mode, const TSharedPtr< IToolkitHost >& InitToolkitHost, const TArray< TWeakObjectPtr< UObject > >& ObjectsToEdit )
{
TArray< UObject* > RawObjectsToEdit;
for( auto ObjectIter = ObjectsToEdit.CreateConstIterator(); ObjectIter; ++ObjectIter )
{
RawObjectsToEdit.Add( ObjectIter->Get() );
}
return FPropertyEditorToolkit::CreateEditor( Mode, InitToolkitHost, RawObjectsToEdit );
}
TSharedRef<IPropertyChangeListener> FPropertyEditorModule::CreatePropertyChangeListener()
{
return MakeShareable( new FPropertyChangeListener );
}
void FPropertyEditorModule::RegisterCustomClassLayout( FName ClassName, FOnGetDetailCustomizationInstance DetailLayoutDelegate )
{
if (ClassName != NAME_None)
{
FDetailLayoutCallback Callback;
Callback.DetailLayoutDelegate = DetailLayoutDelegate;
// @todo: DetailsView: Fix me: this specifies the order in which detail layouts should be queried
Callback.Order = ClassNameToDetailLayoutNameMap.Num();
ClassNameToDetailLayoutNameMap.Add(ClassName, Callback);
}
}
void FPropertyEditorModule::UnregisterCustomClassLayout( FName ClassName )
{
if (ClassName.IsValid() && (ClassName != NAME_None))
{
ClassNameToDetailLayoutNameMap.Remove(ClassName);
}
}
void FPropertyEditorModule::RegisterCustomPropertyTypeLayout( FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate, TSharedPtr<IPropertyTypeIdentifier> Identifier)
{
if( PropertyTypeName != NAME_None )
{
FPropertyTypeLayoutCallback Callback;
Callback.PropertyTypeLayoutDelegate = PropertyTypeLayoutDelegate;
Callback.PropertyTypeIdentifier = Identifier;
FPropertyTypeLayoutCallbackList* LayoutCallbacks = GlobalPropertyTypeToLayoutMap.Find(PropertyTypeName);
if (LayoutCallbacks)
{
LayoutCallbacks->Add(Callback);
}
else
{
FPropertyTypeLayoutCallbackList NewLayoutCallbacks;
NewLayoutCallbacks.Add(Callback);
GlobalPropertyTypeToLayoutMap.Add(PropertyTypeName, NewLayoutCallbacks);
}
}
}
void FPropertyEditorModule::RegisterCustomPropertyTypeLayout(FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate, TSharedPtr<IPropertyTypeIdentifier> Identifier, TSharedPtr<IDetailsView> ForSpecificInstance)
{
if (ForSpecificInstance.IsValid())
{
ForSpecificInstance->RegisterInstancedCustomPropertyTypeLayout(PropertyTypeName, PropertyTypeLayoutDelegate, Identifier);
}
else
{
RegisterCustomPropertyTypeLayout(PropertyTypeName, PropertyTypeLayoutDelegate, Identifier);
}
}
void FPropertyEditorModule::UnregisterCustomPropertyTypeLayout(FName PropertyTypeName, TSharedPtr<IPropertyTypeIdentifier> InIdentifier, TSharedPtr<IDetailsView> ForSpecificInstance)
{
if (ForSpecificInstance.IsValid())
{
ForSpecificInstance->UnregisterInstancedCustomPropertyTypeLayout(PropertyTypeName, InIdentifier);
}
else
{
UnregisterCustomPropertyTypeLayout(PropertyTypeName, InIdentifier);
}
}
void FPropertyEditorModule::UnregisterCustomPropertyTypeLayout( FName PropertyTypeName, TSharedPtr<IPropertyTypeIdentifier> Identifier)
{
if (!PropertyTypeName.IsValid() || (PropertyTypeName == NAME_None))
{
return;
}
FPropertyTypeLayoutCallbackList* LayoutCallbacks = GlobalPropertyTypeToLayoutMap.Find(PropertyTypeName);
if (LayoutCallbacks)
{
LayoutCallbacks->Remove(Identifier);
}
}
bool FPropertyEditorModule::HasUnlockedDetailViews() const
{
uint32 NumUnlockedViews = 0;
for( int32 ViewIndex = 0; ViewIndex < AllDetailViews.Num(); ++ViewIndex )
{
const TWeakPtr<SDetailsView>& DetailView = AllDetailViews[ ViewIndex ];
if( DetailView.IsValid() )
{
TSharedPtr<SDetailsView> DetailViewPin = DetailView.Pin();
if( DetailViewPin->IsUpdatable() && !DetailViewPin->IsLocked() )
{
return true;
}
}
}
return false;
}
/**
* Refreshes property windows with a new list of objects to view
*
* @param NewObjectList The list of objects each property window should view
*/
void FPropertyEditorModule::UpdatePropertyViews( const TArray<UObject*>& NewObjectList )
{
DestroyColorPicker();
TSet<AActor*> ValidObjects;
for( int32 ViewIndex = 0; ViewIndex < AllDetailViews.Num(); ++ViewIndex )
{
if( AllDetailViews[ ViewIndex ].IsValid() )
{
TSharedPtr<SDetailsView> DetailViewPin = AllDetailViews[ ViewIndex ].Pin();
if( DetailViewPin->IsUpdatable() )
{
if( !DetailViewPin->IsLocked() )
{
DetailViewPin->SetObjects(NewObjectList, true);
}
else
{
DetailViewPin->RemoveInvalidObjects();
}
}
}
}
}
void FPropertyEditorModule::ReplaceViewedObjects( const TMap<UObject*, UObject*>& OldToNewObjectMap )
{
// Replace objects from detail views
for( int32 ViewIndex = 0; ViewIndex < AllDetailViews.Num(); ++ViewIndex )
{
if( AllDetailViews[ ViewIndex ].IsValid() )
{
TSharedPtr<SDetailsView> DetailViewPin = AllDetailViews[ ViewIndex ].Pin();
DetailViewPin->ReplaceObjects( OldToNewObjectMap );
}
}
// Replace objects from single views
for( int32 ViewIndex = 0; ViewIndex < AllSinglePropertyViews.Num(); ++ViewIndex )
{
if( AllSinglePropertyViews[ ViewIndex ].IsValid() )
{
TSharedPtr<SSingleProperty> SinglePropPin = AllSinglePropertyViews[ ViewIndex ].Pin();
SinglePropPin->ReplaceObjects( OldToNewObjectMap );
}
}
}
void FPropertyEditorModule::RemoveDeletedObjects( TArray<UObject*>& DeletedObjects )
{
DestroyColorPicker();
// remove deleted objects from detail views
for( int32 ViewIndex = 0; ViewIndex < AllDetailViews.Num(); ++ViewIndex )
{
if( AllDetailViews[ ViewIndex ].IsValid() )
{
TSharedPtr<SDetailsView> DetailViewPin = AllDetailViews[ ViewIndex ].Pin();
DetailViewPin->RemoveDeletedObjects( DeletedObjects );
}
}
// remove deleted object from single property views
for( int32 ViewIndex = 0; ViewIndex < AllSinglePropertyViews.Num(); ++ViewIndex )
{
if( AllSinglePropertyViews[ ViewIndex ].IsValid() )
{
TSharedPtr<SSingleProperty> SinglePropPin = AllSinglePropertyViews[ ViewIndex ].Pin();
SinglePropPin->RemoveDeletedObjects( DeletedObjects );
}
}
}
bool FPropertyEditorModule::IsCustomizedStruct(const UStruct* Struct, const FCustomPropertyTypeLayoutMap& InstancePropertyTypeLayoutMap) const
{
bool bFound = false;
if (Struct && !Struct->IsA<UUserDefinedStruct>())
{
bFound = InstancePropertyTypeLayoutMap.Contains( Struct->GetFName() );
if( !bFound )
{
bFound = GlobalPropertyTypeToLayoutMap.Contains( Struct->GetFName() );
}
}
return bFound;
}
FPropertyTypeLayoutCallback FPropertyEditorModule::GetPropertyTypeCustomization(const FProperty* Property, const IPropertyHandle& PropertyHandle, const FCustomPropertyTypeLayoutMap& InstancedPropertyTypeLayoutMap)
{
if( Property )
{
const FStructProperty* StructProperty = CastField<FStructProperty>(Property);
bool bStructProperty = StructProperty && StructProperty->Struct;
const bool bUserDefinedStruct = bStructProperty && StructProperty->Struct->IsA<UUserDefinedStruct>();
bStructProperty &= !bUserDefinedStruct;
const UEnum* Enum = nullptr;
if (const FByteProperty* ByteProperty = CastField<FByteProperty>(Property))
{
Enum = ByteProperty->Enum;
}
else if (const FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))
{
Enum = EnumProperty->GetEnum();
}
if (Enum && Enum->IsA<UUserDefinedEnum>())
{
Enum = nullptr;
}
const FObjectProperty* ObjectProperty = CastField<FObjectProperty>(Property);
const bool bObjectProperty = ObjectProperty != NULL && ObjectProperty->PropertyClass != NULL;
FName PropertyTypeName;
if( bStructProperty )
{
PropertyTypeName = StructProperty->Struct->GetFName();
}
else if( Enum )
{
PropertyTypeName = Enum->GetFName();
}
else if ( bObjectProperty )
{
UClass* PropertyClass = ObjectProperty->PropertyClass;
while (PropertyClass)
{
const FPropertyTypeLayoutCallback& Callback = FindPropertyTypeLayoutCallback(PropertyClass->GetFName(), PropertyHandle, InstancedPropertyTypeLayoutMap);
if (Callback.IsValid())
{
return Callback;
}
PropertyClass = PropertyClass->GetSuperClass();
}
}
else
{
PropertyTypeName = Property->GetClass()->GetFName();
}
return FindPropertyTypeLayoutCallback(PropertyTypeName, PropertyHandle, InstancedPropertyTypeLayoutMap);
}
return FPropertyTypeLayoutCallback();
}
FPropertyTypeLayoutCallback FPropertyEditorModule::FindPropertyTypeLayoutCallback(FName PropertyTypeName, const IPropertyHandle& PropertyHandle, const FCustomPropertyTypeLayoutMap& InstancedPropertyTypeLayoutMap)
{
if (PropertyTypeName != NAME_None)
{
const FPropertyTypeLayoutCallbackList* LayoutCallbacks = InstancedPropertyTypeLayoutMap.Find( PropertyTypeName );
if( !LayoutCallbacks )
{
LayoutCallbacks = GlobalPropertyTypeToLayoutMap.Find(PropertyTypeName);
}
if ( LayoutCallbacks )
{
const FPropertyTypeLayoutCallback& Callback = LayoutCallbacks->Find(PropertyHandle);
return Callback;
}
}
return FPropertyTypeLayoutCallback();
}
TSharedRef<class IStructureDetailsView> FPropertyEditorModule::CreateStructureDetailView(const FDetailsViewArgs& DetailsViewArgs, const FStructureDetailsViewArgs& StructureDetailsViewArgs, TSharedPtr<FStructOnScope> StructData, const FText& CustomName)
{
TSharedRef<SStructureDetailsView> DetailView =
SNew(SStructureDetailsView)
.DetailsViewArgs(DetailsViewArgs)
.CustomName(CustomName);
struct FStructureDetailsViewFilter
{
static bool HasFilter( const FStructureDetailsViewArgs InStructureDetailsViewArgs )
{
const bool bShowEverything = InStructureDetailsViewArgs.bShowObjects
&& InStructureDetailsViewArgs.bShowAssets
&& InStructureDetailsViewArgs.bShowClasses
&& InStructureDetailsViewArgs.bShowInterfaces;
return !bShowEverything;
}
static bool PassesFilter( const FPropertyAndParent& PropertyAndParent, const FStructureDetailsViewArgs InStructureDetailsViewArgs )
{
const auto ArrayProperty = CastField<FArrayProperty>(&PropertyAndParent.Property);
const auto SetProperty = CastField<FSetProperty>(&PropertyAndParent.Property);
const auto MapProperty = CastField<FMapProperty>(&PropertyAndParent.Property);
// If the property is a container type, the filter should test against the type of the container's contents
const FProperty* PropertyToTest = ArrayProperty ? ArrayProperty->Inner : &PropertyAndParent.Property;
PropertyToTest = SetProperty ? SetProperty->ElementProp : PropertyToTest;
PropertyToTest = MapProperty ? MapProperty->ValueProp : PropertyToTest;
// Meta-data should always be queried off the top-level property, as the inner (for container types) is generated by UHT
const UProperty* MetaDataProperty = &PropertyAndParent.Property;
if( InStructureDetailsViewArgs.bShowClasses && (PropertyToTest->IsA<FClassProperty>() || PropertyToTest->IsA<FSoftClassProperty>()) )
{
return true;
}
if( InStructureDetailsViewArgs.bShowInterfaces && PropertyToTest->IsA<FInterfaceProperty>() )
{
return true;
}
const auto ObjectProperty = CastField<FObjectPropertyBase>(PropertyToTest);
if( ObjectProperty )
{
if( InStructureDetailsViewArgs.bShowAssets )
{
// Is this an "asset" property?
if( PropertyToTest->IsA<FSoftObjectProperty>())
{
return true;
}
// Not an "asset" property, but it may still be a property using an asset class type (such as a raw pointer)
if( ObjectProperty->PropertyClass )
{
if (ensure(MetaDataProperty) && MetaDataProperty->HasMetaData(TEXT("AllowedClasses")))
{
return true;
}
else
{
// We can use the asset tools module to see whether this type has asset actions (which likely means it's an asset class type)
FAssetToolsModule& AssetToolsModule = FAssetToolsModule::GetModule();
return AssetToolsModule.Get().GetAssetTypeActionsForClass(ObjectProperty->PropertyClass).IsValid();
}
}
}
return InStructureDetailsViewArgs.bShowObjects;
}
return true;
}
};
// Only add the filter if we need to exclude things
if (FStructureDetailsViewFilter::HasFilter(StructureDetailsViewArgs))
{
DetailView->SetIsPropertyVisibleDelegate(FIsPropertyVisible::CreateStatic(&FStructureDetailsViewFilter::PassesFilter, StructureDetailsViewArgs));
}
DetailView->SetStructureData(StructData);
return DetailView;
}
TSharedRef<class IPropertyRowGenerator> FPropertyEditorModule::CreatePropertyRowGenerator(const struct FPropertyRowGeneratorArgs& InArgs)
{
return MakeShared<FPropertyRowGenerator>(InArgs, GetThumbnailPool());
}