void SDetailsView::RemoveDeletedObjects( const TArray<UObject*>& DeletedObjects ) { TArray< TWeakObjectPtr< UObject > > NewObjectList; bool bObjectsRemoved = false; for(const TSharedPtr<FComplexPropertyNode>& ComplexRootNode : RootPropertyNodes) { FObjectPropertyNode* RootPropertyNode = ComplexRootNode->AsObjectNode(); // 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 ); } }
TSharedRef<SWidget> SPropertyEditorEditInline::GenerateClassPicker() { FClassViewerInitializationOptions Options; Options.bShowUnloadedBlueprints = true; Options.bShowDisplayNames = true; Options.bShowNoneOption = true; TSharedPtr<FPropertyEditorInlineClassFilter> ClassFilter = MakeShareable( new FPropertyEditorInlineClassFilter ); Options.ClassFilter = ClassFilter; ClassFilter->bAllowAbstract = false; const TSharedRef< FPropertyNode > PropertyNode = PropertyEditor->GetPropertyNode(); UProperty* Property = PropertyNode->GetProperty(); ClassFilter->ObjProperty = Cast<UObjectPropertyBase>( Property ); ClassFilter->IntProperty = Cast<UInterfaceProperty>( Property ); FObjectPropertyNode* ObjectPropertyNode = PropertyNode->FindObjectItemParent(); if( ObjectPropertyNode ) { for ( TPropObjectIterator Itor( ObjectPropertyNode->ObjectIterator() ); Itor; ++Itor ) { UObject* OwnerObject = Itor->Get(); ClassFilter->LimitToIsAOfAllObjects.Add( OwnerObject ); } } Options.PropertyHandle = PropertyEditor->GetPropertyHandle(); FOnClassPicked OnPicked( FOnClassPicked::CreateRaw( this, &SPropertyEditorEditInline::OnClassPicked ) ); return FModuleManager::LoadModuleChecked<FClassViewerModule>("ClassViewer").CreateClassViewer(Options, OnPicked); }
void SDetailsView::RemoveInvalidObjects() { TArray< TWeakObjectPtr< UObject > > ResetArray; bool bAllFound = true; for(const TSharedPtr<FComplexPropertyNode>& ComplexRootNode : RootPropertyNodes) { FObjectPropertyNode* RootPropertyNode = ComplexRootNode->AsObjectNode(); if(RootPropertyNode) { for(TPropObjectIterator Itor(RootPropertyNode->ObjectIterator()); Itor; ++Itor) { TWeakObjectPtr<UObject> Object = *Itor; if(Object.IsValid() && !Object->IsPendingKill()) { ResetArray.Add(Object); } else { bAllFound = false; } } } } if (!bAllFound) { SetObjectArrayPrivate(ResetArray); } }
void SDetailsView::SetObjectPackageOverrides(const TMap<TWeakObjectPtr<UObject>, TWeakObjectPtr<UPackage>>& InMapping) { for(TSharedPtr<FComplexPropertyNode>& ComplexRootNode : RootPropertyNodes) { FObjectPropertyNode* RootNode = ComplexRootNode->AsObjectNode(); if(RootNode) { RootNode->SetObjectPackageOverrides(InMapping); } } }
/** Called at the end of SetObjectArray after we change the objects being observed */ void SDetailsView::PostSetObject() { DestroyColorPicker(); ColorPropertyNode = nullptr; FPropertyNodeInitParams InitParams; InitParams.ParentNode = nullptr; InitParams.Property = nullptr; InitParams.ArrayOffset = 0; InitParams.ArrayIndex = INDEX_NONE; InitParams.bAllowChildren = true; InitParams.bForceHiddenPropertyVisibility = FPropertySettings::Get().ShowHiddenProperties(); switch ( DetailsViewArgs.DefaultsOnlyVisibility ) { case FDetailsViewArgs::EEditDefaultsOnlyNodeVisibility::Hide: InitParams.bCreateDisableEditOnInstanceNodes = false; break; case FDetailsViewArgs::EEditDefaultsOnlyNodeVisibility::Show: InitParams.bCreateDisableEditOnInstanceNodes = true; break; case FDetailsViewArgs::EEditDefaultsOnlyNodeVisibility::Automatic: InitParams.bCreateDisableEditOnInstanceNodes = HasClassDefaultObject(); break; default: check(false); } for( TSharedPtr<FComplexPropertyNode>& ComplexRootNode : RootPropertyNodes ) { FObjectPropertyNode* RootPropertyNode = ComplexRootNode->AsObjectNode(); RootPropertyNode->InitNode( InitParams ); // Restore existing expanded items RestoreExpandedItems(ComplexRootNode.ToSharedRef()); } UpdatePropertyMaps(); for( auto ExternalRootNode : ExternalRootPropertyNodes ) { if( ExternalRootNode.IsValid() ) { RestoreExpandedItems( ExternalRootNode.Pin().ToSharedRef() ); } } UpdateFilteredDetails(); }
/** Called before during SetObjectArray before we change the objects being observed */ void SDetailsView::PreSetObject(int32 InNewNumObjects) { // Save existing expanded items first for(TSharedPtr<FComplexPropertyNode>& RootNode : RootPropertyNodes) { SaveExpandedItems(RootNode.ToSharedRef()); RootNodesPendingKill.Add(RootNode); FObjectPropertyNode* RootObjectNode = RootNode->AsObjectNode(); RootObjectNode->RemoveAllObjects(); RootObjectNode->ClearCachedReadAddresses(true); RootObjectNode->ClearObjectPackageOverrides(); } for( auto ExternalRootNode : ExternalRootPropertyNodes ) { if( ExternalRootNode.IsValid() ) { SaveExpandedItems( ExternalRootNode.Pin().ToSharedRef() ); FComplexPropertyNode* ComplexNode = ExternalRootNode.Pin()->AsComplexNode(); if(ComplexNode) { ComplexNode->Disconnect(); } } } ExternalRootPropertyNodes.Empty(); RootPropertyNodes.Empty(InNewNumObjects); if(DetailsViewArgs.bAllowMultipleTopLevelObjects) { for(int32 NewRootIndex = 0; NewRootIndex < InNewNumObjects; ++NewRootIndex) { RootPropertyNodes.Add(MakeShareable(new FObjectPropertyNode)); } } else { RootPropertyNodes.Add(MakeShareable(new FObjectPropertyNode)); } SelectedActors.Empty(); SelectedObjects.Empty(); }
void SDetailsView::ReplaceObjects( const TMap<UObject*, UObject*>& OldToNewObjectMap ) { TArray< TWeakObjectPtr< UObject > > NewObjectList; bool bObjectsReplaced = false; TArray< FObjectPropertyNode* > ObjectNodes; for(TSharedPtr<FComplexPropertyNode>& RootNode : RootPropertyNodes) { PropertyEditorHelpers::CollectObjectNodes(RootNode, 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->IsRootNode() ) { // 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->IsRootNode() ) { // 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 SPropertyEditorEditInline::OnClassPicked(UClass* InClass) { TArray<FObjectBaseAddress> ObjectsToModify; TArray<FString> NewValues; const TSharedRef< FPropertyNode > PropertyNode = PropertyEditor->GetPropertyNode(); FObjectPropertyNode* ObjectNode = PropertyNode->FindObjectItemParent(); if( ObjectNode ) { for ( TPropObjectIterator Itor( ObjectNode->ObjectIterator() ) ; Itor ; ++Itor ) { FString NewValue; if (InClass) { UObject* Object = Itor->Get(); UObject* UseOuter = (InClass->IsChildOf(UClass::StaticClass()) ? Cast<UClass>(Object)->GetDefaultObject() : Object); EObjectFlags MaskedOuterFlags = UseOuter ? UseOuter->GetMaskedFlags(RF_PropagateToSubObjects) : RF_NoFlags; if (UseOuter && UseOuter->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject)) { MaskedOuterFlags |= RF_ArchetypeObject; } UObject* NewObject = StaticConstructObject(InClass, UseOuter, NAME_None, MaskedOuterFlags, NULL); NewValue = NewObject->GetPathName(); } else { NewValue = FName(NAME_None).ToString(); } NewValues.Add(NewValue); } const TSharedRef< IPropertyHandle > PropertyHandle = PropertyEditor->GetPropertyHandle(); PropertyHandle->SetPerObjectValues( NewValues ); // Force a rebuild of the children when this node changes PropertyNode->RequestRebuildChildren(); ComboButton->SetIsOpen(false); } }
void SDetailsViewBase::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, EPropertyChangeType::ValueSet); PinnedColorPropertyNode->NotifyPostChange(ChangeEvent, GetNotifyHook()); } } }
void FDetailLayoutBuilderImpl::GetObjectsBeingCustomized( TArray< TWeakObjectPtr<UObject> >& OutObjects ) const { OutObjects.Empty(); // The class to find properties in defaults to the class currently being customized FName ClassName = CurrentCustomizationClass ? CurrentCustomizationClass->GetFName() : NAME_None; if( ClassName != NAME_None && CurrentCustomizationVariableName != NAME_None ) { // If this fails there are no properties associated with the class name provided FClassInstanceToPropertyMap* ClassInstanceToPropertyMapPtr = PropertyMap.Find( ClassName ); if( ClassInstanceToPropertyMapPtr ) { FClassInstanceToPropertyMap& ClassInstanceToPropertyMap = *ClassInstanceToPropertyMapPtr; FPropertyNodeMap* PropertyNodeMapPtr = ClassInstanceToPropertyMap.Find( CurrentCustomizationVariableName ); if( PropertyNodeMapPtr ) { FPropertyNodeMap& PropertyNodeMap = *PropertyNodeMapPtr; FObjectPropertyNode* ParentObjectProperty = PropertyNodeMap.ParentProperty ? PropertyNodeMap.ParentProperty->AsObjectNode() : NULL; if (ParentObjectProperty) { for (int32 ObjectIndex = 0; ObjectIndex < ParentObjectProperty->GetNumObjects(); ++ObjectIndex) { OutObjects.Add(ParentObjectProperty->GetUObject(ObjectIndex)); } } } } } else { OutObjects = DetailsView.GetSelectedObjects(); } }
void SDetailsView::ForceRefresh() { TArray< TWeakObjectPtr< UObject > > NewObjectList; const FRootPropertyNodeList& RootNodes = GetRootNodes(); for(const TSharedPtr<FComplexPropertyNode>& ComplexRootNode : RootNodes) { FObjectPropertyNode* RootNode = ComplexRootNode->AsObjectNode(); if(RootNode) { // Simply re-add the same existing objects to cause a refresh for(TPropObjectIterator Itor(RootNode->ObjectIterator()); Itor; ++Itor) { TWeakObjectPtr<UObject> Object = *Itor; if(Object.IsValid()) { NewObjectList.Add(Object.Get()); } } } } SetObjectArrayPrivate( NewObjectList ); }
void SDetailsViewBase::UpdateSinglePropertyMapRecursive(FPropertyNode& InNode, FDetailLayoutData& LayoutData, FName CurCategory, FComplexPropertyNode* CurObjectNode, bool bEnableFavoriteSystem, bool bUpdateFavoriteSystemOnly) { FDetailLayoutBuilderImpl& DetailLayout = *LayoutData.DetailLayout; UProperty* ParentProperty = InNode.GetProperty(); UStructProperty* ParentStructProp = Cast<UStructProperty>(ParentProperty); for(int32 ChildIndex = 0; ChildIndex < InNode.GetNumChildNodes(); ++ChildIndex) { //Use the original value for each child bool LocalUpdateFavoriteSystemOnly = bUpdateFavoriteSystemOnly; 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. UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CurCategory, ObjNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly); } else if(CategoryNode) { if(!LocalUpdateFavoriteSystemOnly) { FName InstanceName = NAME_None; FName CategoryName = CurCategory; FString CategoryDelimiterString; CategoryDelimiterString.AppendChar(FPropertyNodeConstants::CategoryDelimiterChar); if(CurCategory != NAME_None && CategoryNode->GetCategoryName().ToString().Contains(CategoryDelimiterString)) { // This property is child of another property so add it to the parent detail category FDetailCategoryImpl& CategoryImpl = DetailLayout.DefaultCategory(CategoryName); CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName); } } // For category nodes, we just set the current category and recurse through the children UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CategoryNode->GetCategoryName(), CurObjectNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly); } 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; const UStruct* Struct = StructProperty ? StructProperty->Struct : NULL; const UStruct* ParentStruct = ParentStructProp ? ParentStructProp->Struct : NULL; if(Struct || ParentStruct) { FPropertyEditorModule& ParentPlugin = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor"); if(Struct) { bIsCustomizedStruct = ParentPlugin.IsCustomizedStruct(Struct, SharedThis(this)); } if(ParentStruct) { bIsChildOfCustomizedStruct = ParentPlugin.IsCustomizedStruct(ParentStruct, SharedThis(this)); } } // 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 = ChildNode.HasNodeFlags(EPropertyNodeFlags::ShowInnerObjectProperties) || SPropertyEditorEditInline::Supports(&ChildNode, ChildNode.GetArrayIndex()); // Is this a property of a container property bool bIsChildOfContainer = PropertyEditorHelpers::IsChildOfArray(ChildNode) || PropertyEditorHelpers::IsChildOfSet(ChildNode) || PropertyEditorHelpers::IsChildOfMap(ChildNode); // Edit inline new properties should be visible by default bVisibleByDefault |= bIsEditInlineNew; // Children of arrays are not visible directly, bVisibleByDefault &= !bIsChildOfContainer; FPropertyAndParent PropertyAndParent(*Property, ParentProperty); const bool bIsUserVisible = IsPropertyVisible(PropertyAndParent); // 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 && !LocalUpdateFavoriteSystemOnly) { // Add any object classes with properties so we can ask them for custom property layouts later LayoutData.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 if(!bIsChildOfCustomizedStruct && !bIsChildOfContainer && !LocalUpdateFavoriteSystemOnly) { // Get the class property map FClassInstanceToPropertyMap& ClassInstanceMap = LayoutData.ClassToPropertyMap.FindOrAdd(Property->GetOwnerStruct()->GetFName()); FPropertyNodeMap& PropertyNodeMap = ClassInstanceMap.FindOrAdd(InstanceName); if(!PropertyNodeMap.ParentProperty) { PropertyNodeMap.ParentProperty = CurObjectNode; } else { ensure(PropertyNodeMap.ParentProperty == CurObjectNode); } checkSlow(!PropertyNodeMap.Contains(Property->GetFName())); PropertyNodeMap.Add(Property->GetFName(), ChildNodePtr); } bool bCanDisplayFavorite = false; 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; } if(!LocalUpdateFavoriteSystemOnly) { if(IsPropertyReadOnly(PropertyAndParent)) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } // Add a property to the default category FDetailCategoryImpl& CategoryImpl = DetailLayout.DefaultCategory(CategoryName); CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName); } bCanDisplayFavorite = true; if(bEnableFavoriteSystem) { if(bIsCustomizedStruct) { bCanDisplayFavorite = false; //CustomizedStruct child are not categorize since they are under an object but we have to put them in favorite category if the user want to favorite them LocalUpdateFavoriteSystemOnly = true; } else if(ChildNodePtr->IsFavorite()) { //Find or create the favorite category, we have to duplicate favorite property row under this category FString CategoryFavoritesName = TEXT("Favorites"); FName CatFavName = *CategoryFavoritesName; FDetailCategoryImpl& CategoryFavImpl = DetailLayout.DefaultCategory(CatFavName); CategoryFavImpl.SetSortOrder(0); CategoryFavImpl.SetCategoryAsSpecialFavorite(); //Add the property to the favorite FObjectPropertyNode *RootObjectParent = ChildNodePtr->FindRootObjectItemParent(); FName RootInstanceName = NAME_None; if(RootObjectParent != nullptr) { RootInstanceName = RootObjectParent->GetObjectBaseClass()->GetFName(); } if(LocalUpdateFavoriteSystemOnly) { if(IsPropertyReadOnly(PropertyAndParent)) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } else { //If the parent has a condition that is not met, make the child as readonly FDetailLayoutCustomization ParentTmpCustomization; ParentTmpCustomization.PropertyRow = MakeShareable(new FDetailPropertyRow(InNode.AsShared(), CategoryFavImpl.AsShared())); if(ParentTmpCustomization.PropertyRow->GetPropertyEditor()->IsPropertyEditingEnabled() == false) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } } } //Duplicate the row CategoryFavImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), RootInstanceName); } if(bIsStruct) { LocalUpdateFavoriteSystemOnly = true; } } } ChildNodePtr->SetCanDisplayFavorite(bCanDisplayFavorite); 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 && !bIsChildOfContainer // Do not recurse into containers, the children are drawn by the container 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 || LocalUpdateFavoriteSystemOnly) { // Built in struct properties or children of arras UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CurCategory, CurObjectNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly); } } } } }
void SDetailsView::SetObjectArrayPrivate(const TArray< TWeakObjectPtr< UObject > >& InObjects) { double StartTime = FPlatformTime::Seconds(); PreSetObject(InObjects.Num()); // Selected actors for building SelectedActorInfo TArray<AActor*> SelectedRawActors; bViewingClassDefaultObject = InObjects.Num() > 0 ? true : 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 ); if(DetailsViewArgs.bAllowMultipleTopLevelObjects) { check(RootPropertyNodes.Num() == InObjects.Num()); RootPropertyNodes[ObjectIndex]->AsObjectNode()->AddObject( Object.Get() ); } else { RootPropertyNodes[0]->AsObjectNode()->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.NameAreaSettings != FDetailsViewArgs::ActorsUseNameArea && DetailsViewArgs.NameAreaSettings != FDetailsViewArgs::ComponentsAndActorsUseNameArea ) { NameArea->Refresh( SelectedObjects ); } else { NameArea->Refresh( SelectedActors, SelectedObjects, DetailsViewArgs.NameAreaSettings ); } // 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( GetNumObjects() == 0 ) { Title = NSLOCTEXT("PropertyView", "NothingSelectedTitle", "Nothing selected").ToString(); } else if( GetNumObjects() == 1 && RootPropertyNodes[0]->AsObjectNode()->GetNumObjects() > 0) { // if the object is the default metaobject for a UClass, use the UClass's name instead UObject* Object = RootPropertyNodes[0]->AsObjectNode()->GetUObject(0); 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 != nullptr) { // Use the friendly label for this actor ObjectName = Actor->GetActorLabel(); } } Title = ObjectName; } else if(DetailsViewArgs.bAllowMultipleTopLevelObjects) { Title = FString::Printf(*NSLOCTEXT("PropertyView", "MultipleToLevelObjectsSelected", "%i selected").ToString(), GetNumObjects()); } else { FObjectPropertyNode* RootPropertyNode = RootPropertyNodes[0]->AsObjectNode(); Title = FString::Printf( *NSLOCTEXT("PropertyView", "MultipleSelected", "%s (%i selected)").ToString(), *RootPropertyNode->GetObjectBaseClass()->GetName(), RootPropertyNode->GetNumObjects() ); } OnObjectArrayChanged.ExecuteIfBound(Title, InObjects); double ElapsedTime = FPlatformTime::Seconds() - StartTime; }
bool SDetailsView::ShouldSetNewObjects(const TArray< TWeakObjectPtr< UObject > >& InObjects) const { bool bShouldSetObjects = false; const bool bHadBSPBrushSelected = SelectedActorInfo.bHaveBSPBrush; if( bHadBSPBrushSelected == true ) { // If a BSP brush was selected we need to refresh because surface could have been selected and the object set not updated bShouldSetObjects = true; } else if( InObjects.Num() != GetNumObjects() ) { // If the object arrays differ in size then at least one object is different so we must reset bShouldSetObjects = true; } else if(InObjects.Num() == 0) { // User is likely resetting details panel 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); if(DetailsViewArgs.bAllowMultipleTopLevelObjects) { // For multiple top level node support, if the single object in each node is not found in the new object set // then we need to refresh for(int32 RootNodeIndex = 0; RootNodeIndex < RootPropertyNodes.Num(); ++RootNodeIndex) { FObjectPropertyNode* RootPropertyNode = RootPropertyNodes[RootNodeIndex]->AsObjectNode(); if(RootPropertyNode && RootPropertyNode->GetNumObjects() > 0) { if(!NewObjects.Contains(RootPropertyNode->GetUObject(0))) { bShouldSetObjects = true; break; } } else { bShouldSetObjects = true; break; } } } else { ensure(RootPropertyNodes.Num() == 1); FObjectPropertyNode* RootPropertyNode = RootPropertyNodes[0]->AsObjectNode(); if( RootPropertyNode ) { 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; } } } else { bShouldSetObjects = true; } } } if (!bShouldSetObjects && AssetSelectionUtils::IsAnySurfaceSelected(nullptr)) { bShouldSetObjects = true; } return bShouldSetObjects; }