We’ve noticed a bit of an oddity with the way that Named Slots have User Widgets contained within initialized.
Since named slot contents are spliced into the widget tree after child instanced User Widgets are initialized in UUserWidget::DuplicateAndInitializeFromWidgetTree, if their widget trees contain User Widgets then they won’t be initialized until something external triggers initialization. For us, this is often adding the high level widget to a panel or the viewport, which triggers UUserWidget::RebuildWidget() and what appears to be a failsafe initialization call (given the comment present there).
We’ve experimented with running the same instanced subobject initialization for named slots within the SetContentWidgetForNamedSlot lambda, copying the pattern from from earlier in the function, and simply operating on the named slot instancing graph:
`auto SetContentWidgetForNamedSlot = [this,&ConflictingWidgetNames](FName NamedSlotName, UWidget* TemplateSlotContent)
{
FObjectInstancingGraph NamedSlotInstancingGraph;
NamedSlotInstancingGraph.AddNewObject(WidgetTree, TemplateSlotContent->GetTypedOuter());
FName TemplateSlotContentName = TemplateSlotContent->GetFName();
if (ConflictingWidgetNames == nullptr || !ConflictingWidgetNames->Contains(TemplateSlotContentName))
{
UWidget* Content = NewObject(WidgetTree, TemplateSlotContent->GetClass(), TemplateSlotContentName, RF_Transactional, TemplateSlotContent, false, &NamedSlotInstancingGraph);
Content->SetFlags(RF_Transient | RF_DuplicateTransient);
//$$ - BEGIN: Modified here to perform the same logic earlier in UUserWidget::DuplicateAndInitializeFromWidgetTree that the owning widget does
NamedSlotInstancingGraph.ForEachObjectInstance([this](UObject* Instanced) {
#if WITH_EDITOR
if (UWidget* InstancedWidget = Cast(Instanced))
{
InstancedWidget->SetDesignerFlags(GetDesignerFlags());
}
#endif
if (UUserWidget* InstancedSubUserWidget = Cast(Instanced))
{
InstancedSubUserWidget->SetPlayerContext(GetPlayerContext());
InstancedSubUserWidget->Initialize();
}
});
//$$ - END
SetContentForSlot(NamedSlotName, Content);
}
else
{
SetContentForSlot(NamedSlotName, nullptr);
}
};`
Does this seem like a reasonable approach to ensure named slot content gets initialized? Or is named slot initialization intentionally left out of this flow for some reason?