virtual void HandleObjectReference(UObject*& InObject, const UObject* InReferencingObject, const UProperty* InReferencingProperty) override { UObject* Object = InObject; if (!Object || Object->IsA<UBlueprint>()) { return; } UClass* ActualClass = Cast<UClass>(Dependencies.GetActualStruct()); UStruct* CurrentlyConvertedStruct = ActualClass ? Dependencies.FindOriginalClass(ActualClass) : Dependencies.GetActualStruct(); ensure(CurrentlyConvertedStruct); if (Object == CurrentlyConvertedStruct) { return; } { auto ObjAsField = Cast<UField>(Object); if (!ObjAsField) { const bool bTransientObject = (Object->HasAnyFlags(RF_Transient) && !Object->IsIn(CurrentlyConvertedStruct)) || Object->IsIn(GetTransientPackage()); if (bTransientObject) { return; } ObjAsField = Object->GetClass(); } if (ObjAsField && !ObjAsField->HasAnyFlags(RF_ClassDefaultObject)) { if (ObjAsField->IsA<UProperty>()) { ObjAsField = ObjAsField->GetOwnerStruct(); } if (ObjAsField->IsA<UFunction>()) { ObjAsField = ObjAsField->GetOwnerClass(); } IncludeTheHeaderInBody(ObjAsField); } } if ((Object->IsAsset() || Object->IsA<UBlueprintGeneratedClass>()) && !Object->IsIn(CurrentlyConvertedStruct)) { return; } auto OwnedByAnythingInHierarchy = [&]()->bool { for (UStruct* IterStruct = CurrentlyConvertedStruct; IterStruct; IterStruct = IterStruct->GetSuperStruct()) { if (Object->IsIn(IterStruct)) { return true; } UClass* IterClass = Cast<UClass>(IterStruct); UObject* CDO = IterClass ? IterClass->GetDefaultObject(false) : nullptr; if (CDO && Object->IsIn(CDO)) { return true; } } return false; }; if (!Object->IsA<UField>() && !Object->HasAnyFlags(RF_ClassDefaultObject) && !OwnedByAnythingInHierarchy()) { Object = Object->GetClass(); } FindReferencesForNewObject(Object); }
void SDetailsViewBase::SaveExpandedItems() { auto RootPropertyNode = GetRootNode(); check(RootPropertyNode.IsValid()); UStruct* BestBaseStruct = RootPropertyNode->GetBaseStructure(); TArray<FString> ExpandedPropertyItems; GetExpandedItems(RootPropertyNode, ExpandedPropertyItems); // Handle spaces in expanded node names by wrapping them in quotes for( FString& String : ExpandedPropertyItems ) { String.InsertAt(0, '"'); String.AppendChar('"'); } 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 (UStruct* Struct = BestBaseStruct; Struct && ((BestBaseStruct == Struct) || (Struct != AActor::StaticClass())); Struct = Struct->GetSuperStruct()) { if (RootPropertyNode->GetNumChildNodes() > 0) { bool bShouldSave = ExpandedPropertyItems.Num() > 0; if (!bShouldSave) { TArray<FString> DummyExpandedPropertyItems; GConfig->GetSingleLineArray(TEXT("DetailPropertyExpansion"), *Struct->GetName(), DummyExpandedPropertyItems, GEditorPerProjectIni); bShouldSave = DummyExpandedPropertyItems.Num() > 0; } if (bShouldSave) { GConfig->SetSingleLineArray(TEXT("DetailPropertyExpansion"), *Struct->GetName(), ExpandedPropertyItems, GEditorPerProjectIni); } } } if (DetailLayout.IsValid() && BestBaseStruct) { bool bShouldSave = !ExpandedCustomItemsString.IsEmpty(); if (!bShouldSave) { FString DummyExpandedCustomItemsString; GConfig->GetString(TEXT("DetailCustomWidgetExpansion"), *BestBaseStruct->GetName(), DummyExpandedCustomItemsString, GEditorPerProjectIni); bShouldSave = !DummyExpandedCustomItemsString.IsEmpty(); } if (bShouldSave) { GConfig->SetString(TEXT("DetailCustomWidgetExpansion"), *BestBaseStruct->GetName(), *ExpandedCustomItemsString, GEditorPerProjectIni); } } }
void SDetailsViewBase::RestoreExpandedItems(TSharedPtr<FPropertyNode> InitialStartNode) { auto RootPropertyNode = GetRootNode(); check(RootPropertyNode.IsValid()); TSharedPtr<FPropertyNode> StartNode = InitialStartNode; if (!StartNode.IsValid()) { StartNode = RootPropertyNode; } ExpandedDetailNodes.Empty(); TArray<FString> ExpandedPropertyItems; FString ExpandedCustomItems; UStruct* BestBaseStruct = RootPropertyNode->GetBaseStructure(); //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 (UStruct* Struct = BestBaseStruct; Struct && ((BestBaseStruct == Struct) || (Struct != AActor::StaticClass())); Struct = Struct->GetSuperStruct()) { GConfig->GetSingleLineArray(TEXT("DetailPropertyExpansion"), *Struct->GetName(), ExpandedPropertyItems, GEditorPerProjectIni); SetExpandedItems(StartNode, ExpandedPropertyItems); } if (BestBaseStruct) { GConfig->GetString(TEXT("DetailCustomWidgetExpansion"), *BestBaseStruct->GetName(), ExpandedCustomItems, GEditorPerProjectIni); TArray<FString> ExpandedCustomItemsArray; ExpandedCustomItems.ParseIntoArray(ExpandedCustomItemsArray, TEXT(","), true); ExpandedDetailNodes.Append(ExpandedCustomItemsArray); } }
void SDetailsViewBase::QueryCustomDetailLayout(FDetailLayoutBuilderImpl& CustomDetailLayout) { FPropertyEditorModule& ParentPlugin = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor"); // Get the registered classes that customize details FCustomDetailLayoutNameMap& GlobalCustomLayoutNameMap = ParentPlugin.ClassNameToDetailLayoutNameMap; UStruct* BaseStruct = GetBaseStruct(); // 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) { FName Key = InstanceIt.Key(); CustomDetailLayout.SetCurrentCustomizationClass(CastChecked<UClass>(Class), 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 (BaseStruct && !QueriedClasses.Contains(BaseStruct)) { ParentClassesToQuery.Add(BaseStruct); ClassesWithProperties.Add(BaseStruct); } // 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) { if (Cast<UClass>(*ParentIt)) { QueryLayoutForClass(CustomDetailLayout, *ParentIt); } } }