/** 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(); }
/** Ticks the property view. This function performs a data consistency check */ void SDetailsViewBase::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->Disconnect(); RootNodePendingKill.Reset(); } // Empty all the customization instances that need to be deleted CustomizationClassInstancesPendingDelete.Empty(); auto RootPropertyNode = GetRootNode(); check(RootPropertyNode.IsValid()); // Purge any objects that are marked pending kill from the object list if (auto ObjectRoot = RootPropertyNode->AsObjectNode()) { ObjectRoot->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(); } if( RootPropertyNode == RootNodePendingKill ) { // Reaquire the root property node. It may have been changed by the deferred actions if something like a blueprint editor forcefully resets a details panel during a posteditchange RootPropertyNode = GetRootNode(); RestoreExpandedItems(); } 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) { ForceRefresh(); // All objects are being reset, no need to validate external nodes bValidateExternalNodes = false; } if (bValidateExternalNodes) { for (int32 NodeIndex = 0; NodeIndex < ExternalRootPropertyNodes.Num(); ++NodeIndex) { TSharedPtr<FPropertyNode> PropertyNode = ExternalRootPropertyNodes[NodeIndex].Pin(); if (PropertyNode.IsValid()) { Result = PropertyNode->EnsureDataIsValid(); if (Result == FPropertyNode::PropertiesChanged || Result == FPropertyNode::EditInlineNewValueChanged) { RestoreExpandedItems(PropertyNode); 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(PropertyNode); UpdateFilteredDetails(); } } else { // Remove the current node if it is no longer valid ExternalRootPropertyNodes.RemoveAt(NodeIndex); --NodeIndex; } } } 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(); } }