2014-03-14 14:13:41 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
# include "PropertyEditorPrivatePCH.h"
# include "AssetSelection.h"
# include "PropertyNode.h"
# include "ItemPropertyNode.h"
# include "CategoryPropertyNode.h"
# include "ObjectPropertyNode.h"
# include "ScopedTransaction.h"
# include "AssetThumbnail.h"
# include "SDetailNameArea.h"
# include "IPropertyUtilities.h"
# include "PropertyEditorHelpers.h"
# include "PropertyEditor.h"
# include "PropertyDetailsUtilities.h"
# include "SPropertyEditorEditInline.h"
# include "ObjectEditorUtils.h"
# define LOCTEXT_NAMESPACE "SDetailsView"
SDetailsView : : ~ SDetailsView ( )
{
SaveExpandedItems ( ) ;
} ;
/**
* Constructs the widget
*
* @ param InArgs Declaration from which to construct this widget .
*/
void SDetailsView : : Construct ( const FArguments & InArgs )
{
DetailsViewArgs = InArgs . _DetailsViewArgs ;
bHasActiveFilter = false ;
bIsLocked = false ;
bViewingClassDefaultObject = false ;
bHasOpenColorPicker = false ;
// Create the root property now
RootPropertyNode = MakeShareable ( new FObjectPropertyNode ) ;
PropertyUtilities = MakeShareable ( new FPropertyDetailsUtilities ( * this ) ) ;
ColumnWidth = 0.65f ;
ColumnSizeData . LeftColumnWidth = TAttribute < float > ( this , & SDetailsView : : OnGetLeftColumnWidth ) ;
ColumnSizeData . RightColumnWidth = TAttribute < float > ( this , & SDetailsView : : OnGetRightColumnWidth ) ;
ColumnSizeData . OnWidthChanged = SSplitter : : FOnSlotResized : : CreateSP ( this , & SDetailsView : : OnSetColumnWidth ) ;
TSharedRef < SScrollBar > ExternalScrollbar =
SNew ( SScrollBar )
. AlwaysShowScrollbar ( true ) ;
FMenuBuilder DetailViewOptions ( true , NULL ) ;
FUIAction ShowOnlyModifiedAction (
FExecuteAction : : CreateSP ( this , & SDetailsView : : OnShowOnlyModifiedClicked ) ,
FCanExecuteAction ( ) ,
FIsActionChecked : : CreateSP ( this , & SDetailsView : : IsShowOnlyModifiedChecked )
) ;
if ( DetailsViewArgs . bShowModifiedPropertiesOption )
{
DetailViewOptions . AddMenuEntry (
LOCTEXT ( " ShowOnlyModified " , " Show Only Modified Properties " ) ,
LOCTEXT ( " ShowOnlyModified_ToolTip " , " Displays only properties which have been changed from their default " ) ,
FSlateIcon ( ) ,
ShowOnlyModifiedAction ,
NAME_None ,
EUserInterfaceActionType : : ToggleButton
) ;
}
FUIAction ShowAllAdvancedAction (
FExecuteAction : : CreateSP ( this , & SDetailsView : : OnShowAllAdvancedClicked ) ,
FCanExecuteAction ( ) ,
FIsActionChecked : : CreateSP ( this , & SDetailsView : : IsShowAllAdvancedChecked )
) ;
DetailViewOptions . AddMenuEntry (
LOCTEXT ( " ShowAllAdvanced " , " Show All Advanced Details " ) ,
LOCTEXT ( " ShowAllAdvanced_ToolTip " , " Shows all advanced detail sections in each category " ) ,
FSlateIcon ( ) ,
ShowAllAdvancedAction ,
NAME_None ,
EUserInterfaceActionType : : ToggleButton
) ;
DetailViewOptions . AddMenuEntry (
LOCTEXT ( " CollapseAll " , " Collapse All Categories " ) ,
LOCTEXT ( " CollapseAll_ToolTip " , " Collapses all root level categories " ) ,
FSlateIcon ( ) ,
FUIAction ( FExecuteAction : : CreateSP ( this , & SDetailsView : : SetRootExpansionStates , /*bExpanded=*/ false , /*bRecurse=*/ false ) ) ) ;
DetailViewOptions . AddMenuEntry (
LOCTEXT ( " ExpandAll " , " Expand All Categories " ) ,
LOCTEXT ( " ExpandAll_ToolTip " , " Expands all root level categories " ) ,
FSlateIcon ( ) ,
FUIAction ( FExecuteAction : : CreateSP ( this , & SDetailsView : : SetRootExpansionStates , /*bExpanded=*/ true , /*bRecurse=*/ false ) ) ) ;
TSharedRef < SHorizontalBox > FilterRow = SNew ( SHorizontalBox )
. Visibility ( this , & SDetailsView : : GetFilterBoxVisibility )
+ SHorizontalBox : : Slot ( )
. FillWidth ( 1 )
. VAlign ( VAlign_Center )
[
// Create the search box
SAssignNew ( SearchBox , SSearchBox )
. OnTextChanged ( this , & SDetailsView : : OnFilterTextChanged )
]
+ SHorizontalBox : : Slot ( )
. Padding ( 4.0f , 0.0f , 0.0f , 0.0f )
. AutoWidth ( )
[
// Create the search box
SNew ( SButton )
. OnClicked ( this , & SDetailsView : : OnOpenRawPropertyEditorClicked )
2014-04-02 18:09:23 -04:00
. IsEnabled ( this , & SDetailsView : : IsPropertyEditingEnabled )
2014-03-14 14:13:41 -04:00
. ToolTipText ( LOCTEXT ( " RawPropertyEditorButtonLabel " , " Open Selection in Property Matrix " ) )
[
SNew ( SImage )
. Image ( FEditorStyle : : GetBrush ( " DetailsView.EditRawProperties " ) )
]
] ;
if ( DetailsViewArgs . bShowOptions )
{
FilterRow - > AddSlot ( )
. HAlign ( HAlign_Right )
. AutoWidth ( )
[
SNew ( SComboButton )
. ContentPadding ( 0 )
. ForegroundColor ( FSlateColor : : UseForeground ( ) )
. ButtonStyle ( FEditorStyle : : Get ( ) , " ToggleButton " )
. MenuContent ( )
[
DetailViewOptions . MakeWidget ( )
]
. ButtonContent ( )
[
SNew ( SImage )
. Image ( FEditorStyle : : GetBrush ( " GenericViewButton " ) )
]
] ;
}
ChildSlot
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 0.0f , 0.0f , 4.0f )
[
// Create the name area which does not change when selection changes
SAssignNew ( NameArea , SDetailNameArea , & SelectedObjects )
// the name area is only for actors
. Visibility ( this , & SDetailsView : : GetActorNameAreaVisibility )
. OnLockButtonClicked ( this , & SDetailsView : : OnLockButtonClicked )
. IsLocked ( this , & SDetailsView : : IsLocked )
. ShowLockButton ( DetailsViewArgs . bLockable )
. ShowActorLabel ( DetailsViewArgs . bShowActorLabel )
// only show the selection tip if we're not selecting objects
. SelectionTip ( ! DetailsViewArgs . bHideSelectionTip )
]
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 0.0f , 0.0f , 2.0f )
[
FilterRow
]
+ SVerticalBox : : Slot ( )
. FillHeight ( 1 )
. Padding ( 0 )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
[
ConstructTreeView ( ExternalScrollbar )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
[
SNew ( SBox )
. WidthOverride ( 16.0f )
[
ExternalScrollbar
]
]
]
] ;
}
TSharedRef < SDetailTree > SDetailsView : : ConstructTreeView ( TSharedRef < SScrollBar > & ScrollBar )
{
check ( ! DetailTree . IsValid ( ) | | DetailTree . IsUnique ( ) ) ;
return
SAssignNew ( DetailTree , SDetailTree )
. Visibility ( this , & SDetailsView : : GetTreeVisibility )
. TreeItemsSource ( & RootTreeNodes )
. OnGetChildren ( this , & SDetailsView : : OnGetChildrenForDetailTree )
. OnSetExpansionRecursive ( this , & SDetailsView : : SetNodeExpansionStateRecursive )
. OnGenerateRow ( this , & SDetailsView : : OnGenerateRowForDetailTree )
. OnExpansionChanged ( this , & SDetailsView : : OnItemExpansionChanged )
. SelectionMode ( ESelectionMode : : None )
. ExternalScrollbar ( ScrollBar ) ;
}
void SDetailsView : : SetColorPropertyFromColorPicker ( FLinearColor NewColor )
{
const TSharedPtr < FPropertyNode > PinnedColorPropertyNode = ColorPropertyNode . Pin ( ) ;
if ( ensure ( PinnedColorPropertyNode . IsValid ( ) ) )
{
UProperty * Property = PinnedColorPropertyNode - > GetProperty ( ) ;
check ( Property ) ;
FObjectPropertyNode * ObjectNode = PinnedColorPropertyNode - > FindObjectItemParent ( ) ;
if ( ObjectNode & & ObjectNode - > GetNumObjects ( ) )
{
FScopedTransaction Transaction ( NSLOCTEXT ( " UnrealEd " , " SetColorProperty " , " Set Color Property " ) ) ;
PinnedColorPropertyNode - > NotifyPreChange ( Property , GetNotifyHook ( ) ) ;
FPropertyChangedEvent ChangeEvent ( Property , false , EPropertyChangeType : : ValueSet ) ;
PinnedColorPropertyNode - > NotifyPostChange ( ChangeEvent , GetNotifyHook ( ) ) ;
}
}
}
FReply SDetailsView : : OnOpenRawPropertyEditorClicked ( )
{
FPropertyEditorModule & PropertyEditorModule = FModuleManager : : LoadModuleChecked < FPropertyEditorModule > ( " PropertyEditor " ) ;
PropertyEditorModule . CreatePropertyEditorToolkit ( EToolkitMode : : Standalone , TSharedPtr < IToolkitHost > ( ) , SelectedObjects ) ;
return FReply : : Handled ( ) ;
}
EVisibility SDetailsView : : GetActorNameAreaVisibility ( ) const
{
const bool bVisible = ! DetailsViewArgs . bHideActorNameArea & & ! bViewingClassDefaultObject ;
return bVisible ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
EVisibility SDetailsView : : GetFilterBoxVisibility ( ) const
{
// Visible if we allow search and we have anything to search otherwise collapsed so it doesn't take up room
return DetailsViewArgs . bAllowSearch & & RootPropertyNode - > GetNumObjects ( ) > 0 ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
/**
* Determines whether or not a property should be visible in the default generated detail layout
*
* @ param PropertyNode The property node to check
* @ param ParentNode The parent property node to check
* @ return true if the property should be visible
*/
static bool IsVisibleStandaloneProperty ( const FPropertyNode & PropertyNode , const FPropertyNode & ParentNode )
{
const UProperty * Property = PropertyNode . GetProperty ( ) ;
const UArrayProperty * ParentArrayProperty = Cast < const UArrayProperty > ( ParentNode . GetProperty ( ) ) ;
bool bIsVisibleStandalone = false ;
if ( Property )
{
if ( Property - > IsA ( UObjectPropertyBase : : StaticClass ( ) ) )
{
// Do not add this child node to the current map if its a single object property in a category (serves no purpose for UI)
bIsVisibleStandalone = ! ParentArrayProperty & & ( PropertyNode . GetNumChildNodes ( ) = = 0 | | PropertyNode . GetNumChildNodes ( ) > 1 ) ;
}
else if ( Property - > IsA ( UArrayProperty : : StaticClass ( ) ) | | Property - > ArrayDim > 1 & & PropertyNode . GetArrayIndex ( ) = = INDEX_NONE )
{
// Base array properties are always visible
bIsVisibleStandalone = true ;
}
else
{
bIsVisibleStandalone = true ;
}
}
return bIsVisibleStandalone ;
}
2014-04-23 20:18:55 -04:00
void SDetailsView : : UpdatePropertyMapRecursive ( FPropertyNode & InNode , FDetailLayoutBuilderImpl & InDetailLayout , FName CurCategory , FObjectPropertyNode * CurObjectNode )
2014-03-14 14:13:41 -04:00
{
UProperty * ParentProperty = InNode . GetProperty ( ) ;
UStructProperty * ParentStructProp = Cast < UStructProperty > ( ParentProperty ) ;
for ( int32 ChildIndex = 0 ; ChildIndex < InNode . GetNumChildNodes ( ) ; + + ChildIndex )
{
TSharedPtr < FPropertyNode > ChildNodePtr = InNode . GetChildNode ( ChildIndex ) ;
FPropertyNode & ChildNode = * ChildNodePtr ;
UProperty * Property = ChildNode . GetProperty ( ) ;
{
FObjectPropertyNode * ObjNode = ChildNode . AsObjectNode ( ) ;
FCategoryPropertyNode * CategoryNode = ChildNode . AsCategoryNode ( ) ;
if ( ObjNode )
{
// Currently object property nodes do not provide any useful information other than being a container for its children. We do not draw anything for them.
// When we encounter object property nodes, add their children instead of adding them to the tree.
2014-04-23 20:18:55 -04:00
UpdatePropertyMapRecursive ( ChildNode , InDetailLayout , CurCategory , ObjNode ) ;
2014-03-14 14:13:41 -04:00
}
else if ( CategoryNode )
{
// For category nodes, we just set the current category and recurse through the children
2014-04-23 20:18:55 -04:00
UpdatePropertyMapRecursive ( ChildNode , InDetailLayout , CategoryNode - > GetCategoryName ( ) , CurObjectNode ) ;
2014-03-14 14:13:41 -04:00
}
else
{
// Whether or not the property can be visible in the default detail layout
bool bVisibleByDefault = IsVisibleStandaloneProperty ( ChildNode , InNode ) ;
// Whether or not the property is a struct
UStructProperty * StructProperty = Cast < UStructProperty > ( Property ) ;
bool bIsStruct = StructProperty ! = NULL ;
static FName ShowOnlyInners ( " ShowOnlyInnerProperties " ) ;
bool bIsChildOfCustomizedStruct = false ;
bool bIsCustomizedStruct = false ;
2014-04-23 20:18:55 -04:00
const UStruct * Struct = StructProperty ? StructProperty - > Struct : NULL ;
const UStruct * ParentStruct = ParentStructProp ? ParentStructProp - > Struct : NULL ;
if ( Struct | | ParentStruct )
2014-03-14 14:13:41 -04:00
{
2014-04-23 20:18:55 -04:00
FPropertyEditorModule & ParentPlugin = FModuleManager : : GetModuleChecked < FPropertyEditorModule > ( " PropertyEditor " ) ;
if ( Struct )
{
bIsCustomizedStruct = ParentPlugin . IsCustomizedStruct ( Struct ) ;
}
if ( ParentStruct )
{
bIsChildOfCustomizedStruct = ParentPlugin . IsCustomizedStruct ( ParentStruct ) ;
}
2014-03-14 14:13:41 -04:00
}
// Whether or not to push out struct properties to their own categories or show them inside an expandable struct
bool bPushOutStructProps = bIsStruct & & ! bIsCustomizedStruct & & ! ParentStructProp & & Property - > HasMetaData ( ShowOnlyInners ) ;
// Is the property edit inline new
const bool bIsEditInlineNew = SPropertyEditorEditInline : : Supports ( & ChildNode , ChildNode . GetArrayIndex ( ) ) ;
// Is this a property of an array
bool bIsChildOfArray = PropertyEditorHelpers : : IsChildOfArray ( ChildNode ) ;
// Edit inline new properties should be visible by default
bVisibleByDefault | = bIsEditInlineNew ;
2014-06-02 14:20:58 -04:00
// Children of arrays are not visible directly,
2014-03-14 14:13:41 -04:00
bVisibleByDefault & = ! bIsChildOfArray ;
// See if the user requested specific property visibility
2014-05-16 11:32:46 -04:00
const bool bIsUserVisible = IsPropertyVisible ( Property ) ;
2014-03-14 14:13:41 -04:00
// Inners of customized in structs should not be taken into consideration for customizing. They are not designed to be individually customized when their parent is already customized
if ( ! bIsChildOfCustomizedStruct )
{
// Add any object classes with properties so we can ask them for custom property layouts later
ClassesWithProperties . Add ( Property - > GetOwnerStruct ( ) ) ;
}
// If there is no outer object then the class is the object root and there is only one instance
FName InstanceName = NAME_None ;
if ( CurObjectNode & & CurObjectNode - > GetParentNode ( ) )
{
InstanceName = CurObjectNode - > GetParentNode ( ) - > GetProperty ( ) - > GetFName ( ) ;
}
else if ( ParentStructProp )
{
InstanceName = ParentStructProp - > GetFName ( ) ;
}
// Do not add children of customized in struct properties or arrays
2014-06-02 14:20:58 -04:00
if ( ! bIsChildOfCustomizedStruct & & ! bIsChildOfArray )
2014-03-14 14:13:41 -04:00
{
// Get the class property map
FClassInstanceToPropertyMap & ClassInstanceMap = ClassToPropertyMap . FindOrAdd ( Property - > GetOwnerStruct ( ) - > GetFName ( ) ) ;
FPropertyNodeMap & PropertyNodeMap = ClassInstanceMap . FindOrAdd ( InstanceName ) ;
if ( ! PropertyNodeMap . ParentObjectProperty )
{
PropertyNodeMap . ParentObjectProperty = CurObjectNode ;
}
else
{
ensure ( PropertyNodeMap . ParentObjectProperty = = CurObjectNode ) ;
}
checkSlow ( ! PropertyNodeMap . Contains ( Property - > GetFName ( ) ) ) ;
PropertyNodeMap . Add ( Property - > GetFName ( ) , ChildNodePtr ) ;
}
if ( bVisibleByDefault & & bIsUserVisible & & ! bPushOutStructProps )
{
FName CategoryName = CurCategory ;
// For properties inside a struct, add them to their own category unless they just take the name of the parent struct.
// In that case push them to the parent category
FName PropertyCatagoryName = FObjectEditorUtils : : GetCategoryFName ( Property ) ;
if ( ! ParentStructProp | | ( PropertyCatagoryName ! = ParentStructProp - > Struct - > GetFName ( ) ) )
{
CategoryName = PropertyCatagoryName ;
}
// Add a property to the default category
FDetailCategoryImpl & CategoryImpl = InDetailLayout . DefaultCategory ( CategoryName ) ;
CategoryImpl . AddPropertyNode ( ChildNodePtr . ToSharedRef ( ) , InstanceName ) ;
}
bool bRecurseIntoChildren =
! bIsChildOfCustomizedStruct // Don't recurse into built in struct children, we already know what they are and how to display them
& & ! bIsCustomizedStruct // Don't recurse into customized structs
& & ! bIsChildOfArray // Do not recurse into arrays, the children are drawn by the array property parent
& & ! bIsEditInlineNew // Edit inline new children are not supported for customization yet
& & bIsUserVisible // Properties must be allowed to be visible by a user if they are not then their children are not visible either
& & ( ! bIsStruct | | bPushOutStructProps ) ; // Only recurse into struct properties if they are going to be displayed as standalone properties in categories instead of inside an expandable area inside a category
if ( bRecurseIntoChildren )
{
// Built in struct properties or children of arras
2014-04-23 20:18:55 -04:00
UpdatePropertyMapRecursive ( ChildNode , InDetailLayout , CurCategory , CurObjectNode ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
}
void SDetailsView : : UpdatePropertyMap ( )
{
check ( RootPropertyNode . IsValid ( ) ) ;
// Reset everything
ClassToPropertyMap . Empty ( ) ;
ClassesWithProperties . Empty ( ) ;
// We need to be able to create a new detail layout and properly clean up the old one in the process
check ( ! DetailLayout . IsValid ( ) | | DetailLayout . IsUnique ( ) ) ;
RootTreeNodes . Empty ( ) ;
DetailLayout = MakeShareable ( new FDetailLayoutBuilderImpl ( ClassToPropertyMap , PropertyUtilities . ToSharedRef ( ) , SharedThis ( this ) ) ) ;
// Currently object property nodes do not provide any useful information other than being a container for its children. We do not draw anything for them.
// When we encounter object property nodes, add their children instead of adding them to the tree.
2014-04-23 20:18:55 -04:00
UpdatePropertyMapRecursive ( * RootPropertyNode , * DetailLayout , NAME_None , RootPropertyNode . Get ( ) ) ;
2014-03-14 14:13:41 -04:00
// Ask for custom detail layouts
QueryCustomDetailLayout ( * DetailLayout ) ;
DetailLayout - > GenerateDetailLayout ( ) ;
UpdateFilteredDetails ( ) ;
}
void SDetailsView : : ForceRefresh ( )
{
TArray < TWeakObjectPtr < UObject > > NewObjectList ;
// Simply re-add the same existing objects to cause a refresh
for ( TPropObjectIterator Itor ( RootPropertyNode - > ObjectIterator ( ) ) ; Itor ; + + Itor )
{
TWeakObjectPtr < UObject > Object = * Itor ;
if ( Object . IsValid ( ) )
{
NewObjectList . Add ( Object . Get ( ) ) ;
}
}
SetObjectArrayPrivate ( NewObjectList ) ;
}
void SDetailsView : : SetObjects ( const TArray < UObject * > & InObjects , bool bForceRefresh /* = false*/ )
{
if ( ! IsLocked ( ) )
{
TArray < TWeakObjectPtr < UObject > > ObjectWeakPtrs ;
for ( auto ObjectIter = InObjects . CreateConstIterator ( ) ; ObjectIter ; + + ObjectIter )
{
ObjectWeakPtrs . Add ( * ObjectIter ) ;
}
if ( bForceRefresh | | ShouldSetNewObjects ( ObjectWeakPtrs ) )
{
SetObjectArrayPrivate ( ObjectWeakPtrs ) ;
}
}
}
void SDetailsView : : SetObjects ( const TArray < TWeakObjectPtr < UObject > > & InObjects , bool bForceRefresh /* = false*/ )
{
if ( ! IsLocked ( ) )
{
if ( bForceRefresh | | ShouldSetNewObjects ( InObjects ) )
{
SetObjectArrayPrivate ( InObjects ) ;
}
}
}
void SDetailsView : : SetObject ( UObject * InObject , bool bForceRefresh )
{
TArray < TWeakObjectPtr < UObject > > ObjectWeakPtrs ;
ObjectWeakPtrs . Add ( InObject ) ;
SetObjects ( ObjectWeakPtrs , bForceRefresh ) ;
}
bool SDetailsView : : ShouldSetNewObjects ( const TArray < TWeakObjectPtr < UObject > > & InObjects ) const
{
bool bShouldSetObjects = false ;
if ( InObjects . Num ( ) ! = RootPropertyNode - > GetNumObjects ( ) )
{
// If the object arrys differ in size then at least one object is different so we must reset
bShouldSetObjects = true ;
}
else
{
// Check to see if the objects passed in are different. If not we do not need to set anything
TSet < TWeakObjectPtr < UObject > > NewObjects ;
NewObjects . Append ( InObjects ) ;
for ( TPropObjectIterator Itor ( RootPropertyNode - > ObjectIterator ( ) ) ; Itor ; + + Itor )
{
TWeakObjectPtr < UObject > Object = * Itor ;
if ( Object . IsValid ( ) & & ! NewObjects . Contains ( Object ) )
{
// An existing object is not in the list of new objects to set
bShouldSetObjects = true ;
break ;
}
else if ( ! Object . IsValid ( ) )
{
// An existing object is invalid
bShouldSetObjects = true ;
break ;
}
}
}
return bShouldSetObjects ;
}
void SDetailsView : : SetObjectArrayPrivate ( const TArray < TWeakObjectPtr < UObject > > & InObjects )
{
double StartTime = FPlatformTime : : Seconds ( ) ;
PreSetObject ( ) ;
check ( RootPropertyNode . IsValid ( ) ) ;
// Selected actors for building SelectedActorInfo
TArray < AActor * > SelectedRawActors ;
bViewingClassDefaultObject = false ;
bool bOwnedByLockedLevel = false ;
for ( int32 ObjectIndex = 0 ; ObjectIndex < InObjects . Num ( ) ; + + ObjectIndex )
{
TWeakObjectPtr < UObject > Object = InObjects [ ObjectIndex ] ;
if ( Object . IsValid ( ) )
{
bViewingClassDefaultObject | = Object - > HasAnyFlags ( RF_ClassDefaultObject ) ;
RootPropertyNode - > AddObject ( Object . Get ( ) ) ;
SelectedObjects . Add ( Object ) ;
AActor * Actor = Cast < AActor > ( Object . Get ( ) ) ;
if ( Actor )
{
SelectedActors . Add ( Actor ) ;
SelectedRawActors . Add ( Actor ) ;
}
}
}
if ( InObjects . Num ( ) = = 0 )
{
// Unlock the view automatically if we are viewing nothing
bIsLocked = false ;
}
// Selection changed, refresh the detail area
if ( DetailsViewArgs . bObjectsUseNameArea )
{
NameArea - > Refresh ( SelectedObjects ) ;
}
else
{
NameArea - > Refresh ( SelectedActors ) ;
}
// When selection changes rebuild information about the selection
SelectedActorInfo = AssetSelectionUtils : : BuildSelectedActorInfo ( SelectedRawActors ) ;
// @todo Slate Property Window
//SetFlags(EPropertyWindowFlags::ReadOnly, bOwnedByLockedLevel);
PostSetObject ( ) ;
// Set the title of the window based on the objects we are viewing
// Or call the delegate for handling when the title changed
FString Title ;
if ( ! RootPropertyNode - > GetObjectBaseClass ( ) )
{
Title = NSLOCTEXT ( " PropertyView " , " NothingSelectedTitle " , " Nothing selected " ) . ToString ( ) ;
}
else if ( RootPropertyNode - > GetNumObjects ( ) = = 1 )
{
// if the object is the default metaobject for a UClass, use the UClass's name instead
const UObject * Object = RootPropertyNode - > ObjectConstIterator ( ) - > Get ( ) ;
FString ObjectName = Object - > GetName ( ) ;
if ( Object - > GetClass ( ) - > GetDefaultObject ( ) = = Object )
{
ObjectName = Object - > GetClass ( ) - > GetName ( ) ;
}
else
{
// Is this an actor? If so, it might have a friendly name to display
const AActor * Actor = Cast < const AActor > ( Object ) ;
if ( Actor ! = NULL )
{
// Use the friendly label for this actor
ObjectName = Actor - > GetActorLabel ( ) ;
}
}
Title = ObjectName ;
}
else
{
Title = FString : : Printf ( * NSLOCTEXT ( " PropertyView " , " MultipleSelected " , " %s (%i selected) " ) . ToString ( ) , * RootPropertyNode - > GetObjectBaseClass ( ) - > GetName ( ) , RootPropertyNode - > GetNumObjects ( ) ) ;
}
OnObjectArrayChanged . ExecuteIfBound ( Title , InObjects ) ;
double ElapsedTime = FPlatformTime : : Seconds ( ) - StartTime ;
}
void SDetailsView : : ReplaceObjects ( const TMap < UObject * , UObject * > & OldToNewObjectMap )
{
TArray < TWeakObjectPtr < UObject > > NewObjectList ;
bool bObjectsReplaced = false ;
TArray < FObjectPropertyNode * > ObjectNodes ;
PropertyEditorHelpers : : CollectObjectNodes ( RootPropertyNode , ObjectNodes ) ;
for ( int32 ObjectNodeIndex = 0 ; ObjectNodeIndex < ObjectNodes . Num ( ) ; + + ObjectNodeIndex )
{
FObjectPropertyNode * CurrentNode = ObjectNodes [ ObjectNodeIndex ] ;
// Scan all objects and look for objects which need to be replaced
for ( TPropObjectIterator Itor ( CurrentNode - > ObjectIterator ( ) ) ; Itor ; + + Itor )
{
UObject * Replacement = OldToNewObjectMap . FindRef ( Itor - > Get ( ) ) ;
if ( Replacement & & Replacement - > GetClass ( ) = = Itor - > Get ( ) - > GetClass ( ) )
{
bObjectsReplaced = true ;
if ( CurrentNode = = RootPropertyNode . Get ( ) )
{
// Note: only root objects count for the new object list. Sub-Objects (i.e components count as needing to be replaced but they don't belong in the top level object list
NewObjectList . Add ( Replacement ) ;
}
}
else if ( CurrentNode = = RootPropertyNode . Get ( ) )
{
// Note: only root objects count for the new object list. Sub-Objects (i.e components count as needing to be replaced but they don't belong in the top level object list
NewObjectList . Add ( Itor - > Get ( ) ) ;
}
}
}
if ( bObjectsReplaced )
{
SetObjectArrayPrivate ( NewObjectList ) ;
}
}
void SDetailsView : : RemoveDeletedObjects ( const TArray < UObject * > & DeletedObjects )
{
TArray < TWeakObjectPtr < UObject > > NewObjectList ;
bool bObjectsRemoved = false ;
// Scan all objects and look for objects which need to be replaced
for ( TPropObjectIterator Itor ( RootPropertyNode - > ObjectIterator ( ) ) ; Itor ; + + Itor )
{
if ( DeletedObjects . Contains ( Itor - > Get ( ) ) )
{
// An object we had needs to be removed
bObjectsRemoved = true ;
}
else
{
// If the deleted object list does not contain the current object, its ok to keep it in the list
NewObjectList . Add ( Itor - > Get ( ) ) ;
}
}
// if any objects were replaced update the observed objects
if ( bObjectsRemoved )
{
SetObjectArrayPrivate ( NewObjectList ) ;
}
}
/**
* Removes actors from the property nodes object array which are no longer available
*
* @ param ValidActors The list of actors which are still valid
*/
void SDetailsView : : RemoveInvalidActors ( const TSet < AActor * > & ValidActors )
{
TArray < TWeakObjectPtr < UObject > > ResetArray ;
bool bAllFound = true ;
for ( TPropObjectIterator Itor ( RootPropertyNode - > ObjectIterator ( ) ) ; Itor ; + + Itor )
{
AActor * Actor = Cast < AActor > ( Itor - > Get ( ) ) ;
bool bFound = ValidActors . Contains ( Actor ) ;
// If the selected actor no longer exists, remove it from the property window.
if ( bFound )
{
ResetArray . Add ( Actor ) ;
}
else
{
bAllFound = false ;
}
}
if ( ! bAllFound )
{
SetObjectArrayPrivate ( ResetArray ) ;
}
}
/**
* Recursively gets expanded items for a node
*
* @ param InPropertyNode The node to get expanded items from
* @ param OutExpandedItems List of expanded items that were found
*/
void GetExpandedItems ( TSharedPtr < FPropertyNode > InPropertyNode , TArray < FString > & OutExpandedItems )
{
if ( InPropertyNode - > HasNodeFlags ( EPropertyNodeFlags : : Expanded ) )
{
const bool bWithArrayIndex = true ;
FString Path ;
Path . Empty ( 128 ) ;
InPropertyNode - > GetQualifiedName ( Path , bWithArrayIndex ) ;
OutExpandedItems . Add ( Path ) ;
}
for ( int32 ChildIndex = 0 ; ChildIndex < InPropertyNode - > GetNumChildNodes ( ) ; + + ChildIndex )
{
GetExpandedItems ( InPropertyNode - > GetChildNode ( ChildIndex ) , OutExpandedItems ) ;
}
}
/**
* Recursively sets expanded items for a node
*
* @ param InNode The node to set expanded items on
* @ param OutExpandedItems List of expanded items to set
*/
void SetExpandedItems ( TSharedPtr < FPropertyNode > InPropertyNode , const TArray < FString > & InExpandedItems )
{
if ( InExpandedItems . Num ( ) > 0 )
{
const bool bWithArrayIndex = true ;
FString Path ;
Path . Empty ( 128 ) ;
InPropertyNode - > GetQualifiedName ( Path , bWithArrayIndex ) ;
for ( int32 ItemIndex = 0 ; ItemIndex < InExpandedItems . Num ( ) ; + + ItemIndex )
{
if ( InExpandedItems [ ItemIndex ] = = Path )
{
InPropertyNode - > SetNodeFlags ( EPropertyNodeFlags : : Expanded , true ) ;
break ;
}
}
for ( int32 NodeIndex = 0 ; NodeIndex < InPropertyNode - > GetNumChildNodes ( ) ; + + NodeIndex )
{
SetExpandedItems ( InPropertyNode - > GetChildNode ( NodeIndex ) , InExpandedItems ) ;
}
}
}
void SDetailsView : : SaveExpandedItems ( )
{
UClass * BestBaseClass = RootPropertyNode - > GetObjectBaseClass ( ) ;
TArray < FString > ExpandedPropertyItems ;
GetExpandedItems ( RootPropertyNode , ExpandedPropertyItems ) ;
TArray < FString > ExpandedCustomItems = ExpandedDetailNodes . Array ( ) ;
// Expanded custom items may have spaces but SetSingleLineArray doesnt support spaces (treats it as another element in the array)
// Append a '|' after each element instead
FString ExpandedCustomItemsString ;
for ( auto It = ExpandedDetailNodes . CreateConstIterator ( ) ; It ; + + It )
{
ExpandedCustomItemsString + = * It ;
ExpandedCustomItemsString + = TEXT ( " , " ) ;
}
//while a valid class, and we're either the same as the base class (for multiple actors being selected and base class is AActor) OR we're not down to AActor yet)
for ( UClass * Class = BestBaseClass ; Class & & ( ( BestBaseClass = = Class ) | | ( Class ! = AActor : : StaticClass ( ) ) ) ; Class = Class - > GetSuperClass ( ) )
{
if ( RootPropertyNode - > GetNumChildNodes ( ) > 0 & & ExpandedPropertyItems . Num ( ) > 0 )
{
GConfig - > SetSingleLineArray ( TEXT ( " DetailPropertyExpansion " ) , * Class - > GetName ( ) , ExpandedPropertyItems , GEditorUserSettingsIni ) ;
}
}
if ( DetailLayout . IsValid ( ) & & BestBaseClass & & ! ExpandedCustomItemsString . IsEmpty ( ) )
{
GConfig - > SetString ( TEXT ( " DetailCustomWidgetExpansion " ) , * BestBaseClass - > GetName ( ) , * ExpandedCustomItemsString , GEditorUserSettingsIni ) ;
}
}
void SDetailsView : : RestoreExpandedItems ( TSharedPtr < FPropertyNode > InitialStartNode )
{
TSharedPtr < FPropertyNode > StartNode = InitialStartNode ;
if ( ! StartNode . IsValid ( ) )
{
StartNode = RootPropertyNode ;
}
ExpandedDetailNodes . Empty ( ) ;
TArray < FString > ExpandedPropertyItems ;
FString ExpandedCustomItems ;
UClass * BestBaseClass = RootPropertyNode - > GetObjectBaseClass ( ) ;
//while a valid class, and we're either the same as the base class (for multiple actors being selected and base class is AActor) OR we're not down to AActor yet)
for ( UClass * Class = BestBaseClass ; Class & & ( ( BestBaseClass = = Class ) | | ( Class ! = AActor : : StaticClass ( ) ) ) ; Class = Class - > GetSuperClass ( ) )
{
GConfig - > GetSingleLineArray ( TEXT ( " DetailPropertyExpansion " ) , * Class - > GetName ( ) , ExpandedPropertyItems , GEditorUserSettingsIni ) ;
SetExpandedItems ( StartNode , ExpandedPropertyItems ) ;
}
if ( BestBaseClass )
{
GConfig - > GetString ( TEXT ( " DetailCustomWidgetExpansion " ) , * BestBaseClass - > GetName ( ) , ExpandedCustomItems , GEditorUserSettingsIni ) ;
TArray < FString > ExpandedCustomItemsArray ;
ExpandedCustomItems . ParseIntoArray ( & ExpandedCustomItemsArray , TEXT ( " , " ) , true ) ;
ExpandedDetailNodes . Append ( ExpandedCustomItemsArray ) ;
}
}
/** Called before during SetObjectArray before we change the objects being observed */
void SDetailsView : : PreSetObject ( )
{
ExternalRootPropertyNodes . Empty ( ) ;
// Save existing expanded items first
SaveExpandedItems ( ) ;
RootNodePendingKill = RootPropertyNode ;
RootPropertyNode = MakeShareable ( new FObjectPropertyNode ) ;
SelectedActors . Empty ( ) ;
SelectedObjects . Empty ( ) ;
}
/** Called at the end of SetObjectArray after we change the objects being observed */
void SDetailsView : : PostSetObject ( )
{
DestroyColorPicker ( ) ;
ColorPropertyNode = NULL ;
FPropertyNodeInitParams InitParams ;
InitParams . ParentNode = NULL ;
InitParams . Property = NULL ;
InitParams . ArrayOffset = 0 ;
InitParams . ArrayIndex = INDEX_NONE ;
InitParams . bAllowChildren = true ;
InitParams . bForceHiddenPropertyVisibility = FPropertySettings : : Get ( ) . ShowHiddenProperties ( ) ;
RootPropertyNode - > InitNode ( InitParams ) ;
bool bInitiallySeen = true ;
bool bParentAllowsVisible = true ;
// Restore existing expanded items
RestoreExpandedItems ( ) ;
UpdatePropertyMap ( ) ;
}
void SDetailsView : : QueryLayoutForClass ( FDetailLayoutBuilderImpl & CustomDetailLayout , UStruct * Class )
{
CustomDetailLayout . SetCurrentCustomizationClass ( CastChecked < UClass > ( Class ) , NAME_None ) ;
FPropertyEditorModule & ParentPlugin = FModuleManager : : GetModuleChecked < FPropertyEditorModule > ( " PropertyEditor " ) ;
FCustomDetailLayoutNameMap & GlobalCustomLayoutNameMap = ParentPlugin . ClassNameToDetailLayoutNameMap ;
// Check the instanced map first
FDetailLayoutCallback * Callback = InstancedClassToDetailLayoutMap . Find ( TWeakObjectPtr < UStruct > ( Class ) ) ;
if ( ! Callback )
{
// callback wasn't found in the per instance map, try the global instances instead
Callback = GlobalCustomLayoutNameMap . Find ( Class - > GetFName ( ) ) ;
}
if ( Callback & & Callback - > DetailLayoutDelegate . IsBound ( ) )
{
// Create a new instance of the custom detail layout for the current class
TSharedRef < IDetailCustomization > CustomizationInstance = Callback - > DetailLayoutDelegate . Execute ( ) ;
// Ask for details immediately
CustomizationInstance - > CustomizeDetails ( CustomDetailLayout ) ;
// Save the instance from destruction until we refresh
CustomizationClassInstances . Add ( CustomizationInstance ) ;
}
}
void SDetailsView : : QueryCustomDetailLayout ( FDetailLayoutBuilderImpl & CustomDetailLayout )
{
FPropertyEditorModule & ParentPlugin = FModuleManager : : GetModuleChecked < FPropertyEditorModule > ( " PropertyEditor " ) ;
// Get the registered classes that customize details
FCustomDetailLayoutNameMap & GlobalCustomLayoutNameMap = ParentPlugin . ClassNameToDetailLayoutNameMap ;
UClass * BaseClass = GetBaseClass ( ) ;
// All the current customization instances need to be deleted when it is safe
CustomizationClassInstancesPendingDelete = CustomizationClassInstances ;
CustomizationClassInstances . Empty ( ) ;
//Ask for generic details not specific to an object being viewed
if ( GenericLayoutDelegate . IsBound ( ) )
{
// Create a new instance of the custom detail layout for the current class
TSharedRef < IDetailCustomization > CustomizationInstance = GenericLayoutDelegate . Execute ( ) ;
// Ask for details immediately
CustomizationInstance - > CustomizeDetails ( CustomDetailLayout ) ;
// Save the instance from destruction until we refresh
CustomizationClassInstances . Add ( CustomizationInstance ) ;
}
// Sort them by query order. @todo not good enough
struct FCompareFDetailLayoutCallback
{
FORCEINLINE bool operator ( ) ( const FDetailLayoutCallback & A , const FDetailLayoutCallback & B ) const
{
return A . Order < B . Order ;
}
} ;
TMap < TWeakObjectPtr < UStruct > , FDetailLayoutCallback * > FinalCallbackMap ;
for ( auto ClassIt = ClassesWithProperties . CreateConstIterator ( ) ; ClassIt ; + + ClassIt )
{
// Check the instanced map first
FDetailLayoutCallback * Callback = InstancedClassToDetailLayoutMap . Find ( * ClassIt ) ;
if ( ! Callback )
{
// callback wasn't found in the per instance map, try the global instances instead
Callback = GlobalCustomLayoutNameMap . Find ( ( * ClassIt ) - > GetFName ( ) ) ;
}
if ( Callback )
{
FinalCallbackMap . Add ( * ClassIt , Callback ) ;
}
}
FinalCallbackMap . ValueSort ( FCompareFDetailLayoutCallback ( ) ) ;
TSet < UStruct * > QueriedClasses ;
if ( FinalCallbackMap . Num ( ) > 0 )
{
// Ask each class that we have properties for to customize its layout
for ( auto LayoutIt ( FinalCallbackMap . CreateConstIterator ( ) ) ; LayoutIt ; + + LayoutIt )
{
const TWeakObjectPtr < UStruct > WeakClass = LayoutIt . Key ( ) ;
if ( WeakClass . IsValid ( ) )
{
UStruct * Class = WeakClass . Get ( ) ;
FClassInstanceToPropertyMap & InstancedPropertyMap = ClassToPropertyMap . FindChecked ( Class - > GetFName ( ) ) ;
for ( FClassInstanceToPropertyMap : : TIterator InstanceIt ( InstancedPropertyMap ) ; InstanceIt ; + + InstanceIt )
{
CustomDetailLayout . SetCurrentCustomizationClass ( CastChecked < UClass > ( Class ) , InstanceIt . Key ( ) ) ;
const FOnGetDetailCustomizationInstance & DetailDelegate = LayoutIt . Value ( ) - > DetailLayoutDelegate ;
if ( DetailDelegate . IsBound ( ) )
{
QueriedClasses . Add ( Class ) ;
// Create a new instance of the custom detail layout for the current class
TSharedRef < IDetailCustomization > CustomizationInstance = DetailDelegate . Execute ( ) ;
// Ask for details immediately
CustomizationInstance - > CustomizeDetails ( CustomDetailLayout ) ;
// Save the instance from destruction until we refresh
CustomizationClassInstances . Add ( CustomizationInstance ) ;
}
}
}
}
}
// Ensure that the base class and its parents are always queried
TSet < UStruct * > ParentClassesToQuery ;
if ( BaseClass & & ! QueriedClasses . Contains ( BaseClass ) )
{
ParentClassesToQuery . Add ( BaseClass ) ;
ClassesWithProperties . Add ( BaseClass ) ;
}
// Find base classes of queried classes that were not queried and add them to the query list
// this supports cases where a parent class has no properties but still wants to add customization
for ( auto QueriedClassIt = ClassesWithProperties . CreateConstIterator ( ) ; QueriedClassIt ; + + QueriedClassIt )
{
UStruct * ParentStruct = ( * QueriedClassIt ) - > GetSuperStruct ( ) ;
while ( ParentStruct & & ParentStruct - > IsA ( UClass : : StaticClass ( ) ) & & ! QueriedClasses . Contains ( ParentStruct ) & & ! ClassesWithProperties . Contains ( ParentStruct ) )
{
ParentClassesToQuery . Add ( ParentStruct ) ;
ParentStruct = ParentStruct - > GetSuperStruct ( ) ;
}
}
// Query extra base classes
for ( auto ParentIt = ParentClassesToQuery . CreateConstIterator ( ) ; ParentIt ; + + ParentIt )
{
QueryLayoutForClass ( CustomDetailLayout , * ParentIt ) ;
}
}
void SDetailsView : : UpdateFilteredDetails ( )
{
RootPropertyNode - > FilterNodes ( CurrentFilter . FilterStrings ) ;
RootPropertyNode - > ProcessSeenFlags ( true ) ;
for ( int32 NodeIndex = 0 ; NodeIndex < ExternalRootPropertyNodes . Num ( ) ; + + NodeIndex )
{
TSharedPtr < FObjectPropertyNode > ObjectNode = ExternalRootPropertyNodes [ NodeIndex ] . Pin ( ) ;
if ( ObjectNode . IsValid ( ) )
{
ObjectNode - > FilterNodes ( CurrentFilter . FilterStrings ) ;
ObjectNode - > ProcessSeenFlags ( true ) ;
}
}
if ( DetailLayout . IsValid ( ) )
{
DetailLayout - > FilterDetailLayout ( CurrentFilter ) ;
}
RootTreeNodes = DetailLayout - > GetRootTreeNodes ( ) ;
DetailTree - > RequestTreeRefresh ( ) ;
}
/** Ticks the property view. This function performs a data consistency check */
void SDetailsView : : Tick ( const FGeometry & AllottedGeometry , const double InCurrentTime , const float InDeltaTime )
{
for ( int32 i = 0 ; i < CustomizationClassInstancesPendingDelete . Num ( ) ; + + i )
{
ensure ( CustomizationClassInstancesPendingDelete [ i ] . IsUnique ( ) ) ;
}
if ( RootNodePendingKill . IsValid ( ) )
{
RootNodePendingKill - > RemoveAllObjects ( ) ;
RootNodePendingKill . Reset ( ) ;
}
// Empty all the customization instances that need to be deleted
CustomizationClassInstancesPendingDelete . Empty ( ) ;
// Purge any objects that are marked pending kill from the object list
RootPropertyNode - > PurgeKilledObjects ( ) ;
if ( DeferredActions . Num ( ) > 0 )
{
// Any deferred actions are likely to cause the node tree to be at least partially rebuilt
// Save the expansion state of existing nodes so we can expand them later
SaveExpandedItems ( ) ;
// Execute any deferred actions
for ( int32 ActionIndex = 0 ; ActionIndex < DeferredActions . Num ( ) ; + + ActionIndex )
{
DeferredActions [ ActionIndex ] . ExecuteIfBound ( ) ;
}
DeferredActions . Empty ( ) ;
}
bool bValidateExternalNodes = true ;
FPropertyNode : : DataValidationResult Result = RootPropertyNode - > EnsureDataIsValid ( ) ;
if ( Result = = FPropertyNode : : PropertiesChanged | | Result = = FPropertyNode : : EditInlineNewValueChanged )
{
RestoreExpandedItems ( ) ;
UpdatePropertyMap ( ) ;
}
else if ( Result = = FPropertyNode : : ArraySizeChanged )
{
RestoreExpandedItems ( ) ;
UpdateFilteredDetails ( ) ;
}
else if ( Result = = FPropertyNode : : ObjectInvalid )
{
TArray < TWeakObjectPtr < UObject > > ResetArray ;
for ( TPropObjectIterator Itor ( RootPropertyNode - > ObjectIterator ( ) ) ; Itor ; + + Itor )
{
TWeakObjectPtr < UObject > Object = * Itor ;
if ( Object . IsValid ( ) )
{
ResetArray . Add ( Object . Get ( ) ) ;
}
}
SetObjectArrayPrivate ( ResetArray ) ;
// All objects are being reset, no need to validate external nodes
bValidateExternalNodes = false ;
}
if ( bValidateExternalNodes )
{
for ( int32 NodeIndex = 0 ; NodeIndex < ExternalRootPropertyNodes . Num ( ) ; + + NodeIndex )
{
TSharedPtr < FObjectPropertyNode > ObjectNode = ExternalRootPropertyNodes [ NodeIndex ] . Pin ( ) ;
if ( ObjectNode . IsValid ( ) )
{
Result = ObjectNode - > EnsureDataIsValid ( ) ;
if ( Result = = FPropertyNode : : PropertiesChanged | | Result = = FPropertyNode : : EditInlineNewValueChanged )
{
RestoreExpandedItems ( ObjectNode ) ;
UpdatePropertyMap ( ) ;
// Note this will invalidate all the external root nodes so there is no need to continue
ExternalRootPropertyNodes . Empty ( ) ;
break ;
}
else if ( Result = = FPropertyNode : : ArraySizeChanged )
{
RestoreExpandedItems ( ObjectNode ) ;
UpdateFilteredDetails ( ) ;
}
}
else
{
// Remove the current node if it is no longer valid
ExternalRootPropertyNodes . RemoveAt ( NodeIndex ) ;
- - NodeIndex ;
}
}
}
if ( ThumbnailPool . IsValid ( ) )
{
ThumbnailPool - > Tick ( InDeltaTime ) ;
}
if ( DetailLayout . IsValid ( ) )
{
DetailLayout - > Tick ( InDeltaTime ) ;
}
if ( ! ColorPropertyNode . IsValid ( ) & & bHasOpenColorPicker )
{
// Destroy the color picker window if the color property node has become invalid
DestroyColorPicker ( ) ;
bHasOpenColorPicker = false ;
}
if ( FilteredNodesRequestingExpansionState . Num ( ) > 0 )
{
// change expansion state on the nodes that request it
for ( TMap < TSharedRef < IDetailTreeNode > , bool > : : TConstIterator It ( FilteredNodesRequestingExpansionState ) ; It ; + + It )
{
DetailTree - > SetItemExpansion ( It . Key ( ) , It . Value ( ) ) ;
}
FilteredNodesRequestingExpansionState . Empty ( ) ;
}
}
/**
* Creates the color picker window for this property view .
*
* @ param Node The slate property node to edit .
* @ param bUseAlpha Whether or not alpha is supported
*/
void SDetailsView : : CreateColorPickerWindow ( const TSharedRef < FPropertyEditor > & PropertyEditor , bool bUseAlpha )
{
const TSharedRef < FPropertyNode > PinnedColorPropertyNode = PropertyEditor - > GetPropertyNode ( ) ;
ColorPropertyNode = PinnedColorPropertyNode ;
UProperty * Property = PinnedColorPropertyNode - > GetProperty ( ) ;
check ( Property ) ;
FReadAddressList ReadAddresses ;
PinnedColorPropertyNode - > GetReadAddress ( false , ReadAddresses , false ) ;
TArray < FLinearColor * > LinearColor ;
TArray < FColor * > DWORDColor ;
for ( int32 ColorIndex = 0 ; ColorIndex < ReadAddresses . Num ( ) ; + + ColorIndex )
{
const uint8 * Addr = ReadAddresses . GetAddress ( ColorIndex ) ;
if ( Addr )
{
if ( Cast < UStructProperty > ( Property ) - > Struct - > GetFName ( ) = = NAME_Color )
{
DWORDColor . Add ( ( FColor * ) Addr ) ;
}
else
{
check ( Cast < UStructProperty > ( Property ) - > Struct - > GetFName ( ) = = NAME_LinearColor ) ;
LinearColor . Add ( ( FLinearColor * ) Addr ) ;
}
}
}
bHasOpenColorPicker = true ;
FColorPickerArgs PickerArgs ;
PickerArgs . ParentWidget = AsShared ( ) ;
PickerArgs . bUseAlpha = bUseAlpha ;
PickerArgs . DisplayGamma = TAttribute < float > : : Create ( TAttribute < float > : : FGetter : : CreateUObject ( GEngine , & UEngine : : GetDisplayGamma ) ) ;
PickerArgs . ColorArray = & DWORDColor ;
PickerArgs . LinearColorArray = & LinearColor ;
PickerArgs . OnColorCommitted = FOnLinearColorValueChanged : : CreateSP ( this , & SDetailsView : : SetColorPropertyFromColorPicker ) ;
PickerArgs . OnColorPickerWindowClosed = FOnWindowClosed : : CreateSP ( this , & SDetailsView : : OnColorPickerWindowClosed ) ;
OpenColorPicker ( PickerArgs ) ;
}
void SDetailsView : : SetOnObjectArrayChanged ( FOnObjectArrayChanged OnObjectArrayChangedDelegate )
{
OnObjectArrayChanged = OnObjectArrayChangedDelegate ;
}
const UClass * SDetailsView : : GetBaseClass ( ) const
{
if ( RootPropertyNode . IsValid ( ) )
{
return RootPropertyNode - > GetObjectBaseClass ( ) ;
}
return NULL ;
}
UClass * SDetailsView : : GetBaseClass ( )
{
if ( RootPropertyNode . IsValid ( ) )
{
return RootPropertyNode - > GetObjectBaseClass ( ) ;
}
return NULL ;
}
void SDetailsView : : RegisterInstancedCustomPropertyLayout ( UClass * Class , FOnGetDetailCustomizationInstance DetailLayoutDelegate )
{
check ( Class ) ;
FDetailLayoutCallback Callback ;
Callback . DetailLayoutDelegate = DetailLayoutDelegate ;
// @todo: DetailsView: Fix me: this specifies the order in which detail layouts should be queried
Callback . Order = InstancedClassToDetailLayoutMap . Num ( ) ;
InstancedClassToDetailLayoutMap . Add ( Class , Callback ) ;
}
void SDetailsView : : UnregisterInstancedCustomPropertyLayout ( UClass * Class )
{
check ( Class ) ;
InstancedClassToDetailLayoutMap . Remove ( Class ) ;
}
bool SDetailsView : : SupportsKeyboardFocus ( ) const
{
return DetailsViewArgs . bSearchInitialKeyFocus & & SearchBox - > SupportsKeyboardFocus ( ) & & GetFilterBoxVisibility ( ) = = EVisibility : : Visible ;
}
FReply SDetailsView : : OnKeyboardFocusReceived ( const FGeometry & MyGeometry , const FKeyboardFocusEvent & InKeyboardFocusEvent )
{
FReply Reply = FReply : : Handled ( ) ;
if ( InKeyboardFocusEvent . GetCause ( ) ! = EKeyboardFocusCause : : Cleared )
{
Reply . SetKeyboardFocus ( SearchBox . ToSharedRef ( ) , InKeyboardFocusEvent . GetCause ( ) ) ;
}
return Reply ;
}
void SDetailsView : : AddExternalRootPropertyNode ( TSharedRef < FObjectPropertyNode > ExternalRootNode )
{
ExternalRootPropertyNodes . Add ( ExternalRootNode ) ;
}
bool SDetailsView : : IsCategoryHiddenByClass ( FName CategoryName ) const
{
return RootPropertyNode - > GetHiddenCategories ( ) . Contains ( CategoryName ) ;
}
# undef LOCTEXT_NAMESPACE