static void LoadUObjectForBrush( const FSlateBrush& InBrush ) { // Load the utexture FString Path = InBrush.GetResourceName().ToString(); if( !Path.IsEmpty() ) { FString NewPath = Path.RightChop(FSlateBrush::UTextureIdentifier().Len()); UObject* TextureObject = LoadObject<UTexture2D>(NULL, *NewPath, NULL, LOAD_None, NULL); FSlateBrush* Brush = const_cast<FSlateBrush*>(&InBrush); // Set the texture object to a default texture to prevent constant loading of missing textures if( !TextureObject ) { UE_LOG(LogSlate, Warning, TEXT("Error loading loading UTexture from path: %s not found"), *Path); TextureObject = GEngine->DefaultTexture; } else { // We do this here because this deprecated system of loading textures will not report references and we dont want the Slate RHI resource manager to manage references TextureObject->AddToRoot(); } Brush->SetResourceObject(TextureObject); UE_LOG(LogSlate, Warning, TEXT("The texture:// method of loading UTextures for use in Slate is deprecated. Please convert %s to a Brush Asset"), *Path); } }
void FConfigPropertyHelperDetails::AddEditablePropertyForConfig(IDetailLayoutBuilder& DetailBuilder, const UPropertyConfigFileDisplayRow* ConfigFilePropertyRowObj) { AssociatedConfigFileAndObjectPairings.Add(ConfigFilePropertyRowObj->ConfigFileName, (UObject*)ConfigFilePropertyRowObj); // Add the properties to a property table so we can edit these. IDetailCategoryBuilder& TempCategory = DetailBuilder.EditCategory("TempCategory"); UObject* ConfigEntryObject = StaticDuplicateObject(ConfigEditorPropertyViewCDO, GetTransientPackage(), *(ConfigFilePropertyRowObj->ConfigFileName + TEXT("_cdoDupe"))); ConfigEntryObject->AddToRoot(); FString ExistingConfigEntryValue; static FString SectionName = OriginalProperty->GetOwnerClass()->GetPathName(); static FString PropertyName = ConfigEditorCopyOfEditProperty->GetName(); if (GConfig->GetString(*SectionName, *PropertyName, ExistingConfigEntryValue, ConfigFilePropertyRowObj->ConfigFileName)) { ConfigEditorCopyOfEditProperty->ImportText(*ExistingConfigEntryValue, ConfigEditorCopyOfEditProperty->ContainerPtrToValuePtr<uint8>(ConfigEntryObject), 0, nullptr); } // Cache a reference for future usage. ConfigFileAndPropertySourcePairings.Add(ConfigFilePropertyRowObj->ConfigFileName, (UObject*)ConfigEntryObject); // We need to add a property row for each config file entry. // This allows us to have an editable widget for each config file. TArray<UObject*> ConfigPropertyDisplayObjects; ConfigPropertyDisplayObjects.Add(ConfigEntryObject); if (IDetailPropertyRow* ExternalRow = TempCategory.AddExternalProperty(ConfigPropertyDisplayObjects, ConfigEditorCopyOfEditProperty->GetFName())) { TSharedPtr<SWidget> NameWidget; TSharedPtr<SWidget> ValueWidget; ExternalRow->GetDefaultWidgets(NameWidget, ValueWidget); // Register the Value widget and config file pairing with the config editor. // The config editor needs this to determine what a cell presenter shows. IConfigEditorModule& ConfigEditor = FModuleManager::Get().LoadModuleChecked<IConfigEditorModule>("ConfigEditor"); ConfigEditor.AddExternalPropertyValueWidgetAndConfigPairing(ConfigFilePropertyRowObj->ConfigFileName, ValueWidget); // now hide the property so it is not added to the property display view ExternalRow->Visibility(EVisibility::Hidden); } }
void FUObjectArray::CloseDisregardForGC() { check(IsInGameThread()); check(OpenForDisregardForGC); // Iterate over all class objects and force the default objects to be created. Additionally also // assembles the token reference stream at this point. This is required for class objects that are // not taken into account for garbage collection but have instances that are. // Workaround for Visual Studio 2013 analyzer bug. Using a temporary directly in the range-for // errors if the analyzer is enabled. TObjectRange<UClass> Range; for (UClass* Class : Range) { // Force the default object to be created. Class->GetDefaultObject(); // Force the default object to be constructed if it isn't already // Assemble reference token stream for garbage collection/ RTGC. if (!Class->HasAnyClassFlags(CLASS_TokenStreamAssembled)) { Class->AssembleReferenceTokenStream(); } } if (GIsInitialLoad) { // Iterate over all objects and mark them to be part of root set. int32 NumAlwaysLoadedObjects = 0; int32 NumRootObjects = 0; for (FObjectIterator It; It; ++It) { UObject* Object = *It; if (Object->IsSafeForRootSet()) { NumRootObjects++; Object->AddToRoot(); } else if (Object->IsRooted()) { Object->RemoveFromRoot(); } NumAlwaysLoadedObjects++; } UE_LOG(LogUObjectArray, Log, TEXT("%i objects as part of root set at end of initial load."), NumAlwaysLoadedObjects); if (GUObjectArray.DisregardForGCEnabled()) { UE_LOG(LogUObjectArray, Log, TEXT("%i objects are not in the root set, but can never be destroyed because they are in the DisregardForGC set."), NumAlwaysLoadedObjects - NumRootObjects); } // When disregard for GC pool is closed for the first time, make sure the first GC index is set after the last non-GC index. // We do allow here for some slack if MaxObjectsNotConsideredByGC > (ObjLastNonGCIndex + 1) so that disregard for GC pool // can be re-opened later. ObjFirstGCIndex = FMath::Max(ObjFirstGCIndex, ObjLastNonGCIndex + 1); GUObjectAllocator.BootMessage(); } UE_LOG(LogUObjectArray, Log, TEXT("CloseDisregardForGC: %d/%d objects in disregard for GC pool"), ObjLastNonGCIndex + 1, MaxObjectsNotConsideredByGC); OpenForDisregardForGC = false; GIsInitialLoad = false; }