Download

DetailsCustomization - Embedding all properties of another UObject

I’m customizing the details layout for one of my objects, call it UParentObject. It has a non-property member of type TSet< UChildObject* >. Depending on the selection in a widget I’ve added to the customization, I want to embed in my details panel all properties of a particular UChildObject instance in the set. Is there a straightforward way to do this?

I tried adding a nested IDetailsView, but this messed up the formatting and scrolling pretty badly so I suspect it’s not intended usage.

The behaviour I’m after is precisely the same as how the properties of a component are incorporated when the component property has the EditAnywhere specifier - ie. grouped together and indented, rather than mixed in to the categories of the parent object. I guess I could manually iterate through all fields of the child object and add property rows using AddExternalProperty, but I was just hoping there was already something in place to do this and format it for me in the same way as with components/subobject properties. Any ideas?

Editing non-property members is going to be a chore no matter how you cut it – you lose all of the details customization functionality coming from UProperty and IPropertyHandles.

Were it up to me, I’d strongly question the need for a TSet – especially when dealing with a simple container of pointers. The constant time lookups are nice on paper but rarely necessary. I’ve basically been soured by an instance early on in my career where using a hashed container turned out to be less efficient than a plain old array. The lack of property support for TSet in UE4 only makes this issue worse.

But if you’re adamant about using a set, you could probably define an editor-only TArray, expose that to details customization in a much more straighforward fashion, and override PostEditChangeChainProperty to have your TArray transparently maintain the TSet. But since TSet is not managed by the engine, you’ll also have to manually serialize it so its state can be saved/loaded along with your object.

-Camille

Depending on what you are trying to edit specifically in the object, you can do things like this:


	TArray<UObject*> ObjectList;
	ObjectList.Add(ObjectFromYourSet);
	IDetailPropertyRow* Row = Category.AddExternalProperty(ObjectList, PropertyInObjectFromYourSet->GetFName());

Cheers,
Michael Noland

Thanks for the replies.

Camille, regarding the TSet I think you are probably right. Performance wasn’t actually a consideration, more habit (from STL use) and convenience since I needed uniqueness. However on closer inspection TArray has most of the methods you need to effectively use it as a set anyway, plus of course being reflection enabled. I’ll switch over.
In this case though my aim was not to display properties for the whole set, but rather for a single item in the set, based on selection from another widget. Still, your post has made me realise how I should probably do it, as below.

Michael, yep, I hadn’t tried that yet but had figured I could do it that way. The only issue was, I wanted to display all the properties of the given object, ideally in a standardized way. I am sure there must be the code to do this somewhere already, but since I can’t locate it, I figure a simple workaround would be to simply add a transient UObject property, which I can set to the desired element in my set and let the UProperty details customization take over.

Okay so I tried and apparently that doesn’t work, the UObject property that I add is not being expanded to show its child properties. Michael, is this recursive expansion of child properties implemented at a level above the details customization, such that anything I add in a details customization cannot make use of it and I have to do the recursive enumerating of child properties myself?

Yeah, that is for showing a specific property. Under some circumstances you can tag objects to be displayed effectively inline, but messing with EditInlineNew, etc… is always a hassle. I’d suggest doing something like ‘non-instanced’ tile map components do (PaperTileMapDetailsCustomization.cpp), which gives you full control, although the one downside is that you don’t get access to any existing details customization that would have been applied to the source object:


	// Add all of the properties from the inline tilemap
	if ((TileComponent != nullptr) && (TileComponent->OwnsTileMap()))
	{
		TArray<UObject*> ListOfTileMaps;
		ListOfTileMaps.Add(TileMap);

		for (TFieldIterator<UProperty> PropIt(UPaperTileMap::StaticClass()); PropIt; ++PropIt)
		{
			UProperty* TestProperty = *PropIt;

			if (TestProperty->HasAnyPropertyFlags(CPF_Edit))
			{
				FName CategoryName(*TestProperty->GetMetaData(TEXT("Category")));
				IDetailCategoryBuilder& Category = DetailLayout.EditCategory(CategoryName);

				if (IDetailPropertyRow* ExternalRow = Category.AddExternalProperty(ListOfTileMaps, TestProperty->GetFName()))
				{
					ExternalRow->Visibility(InternalInstanceVis);
				}
			}
		}
	}

Cheers,
Michael Noland

Right, yeah I found that the Instanced specifier expands it out, but that seems like a hack and also forces the UObject property to be editable itself, which I don’t want.
I’ll try the manual route as you suggest. Thanks for the pointer to the example, really appreciate your time!