While dealing with custom editor tool development, I needed to find a slate widget by type that was inside an SCompoundWidget. Surprisingly I couldn’t really find a solution online, so I’ll share my own solution below.
You can easily rewrite this into a generic function.
// in header file
TSharedPtr<class STextBlock> ActorPickerText;
bool SActorIOActionListViewRow::FindActorPickerText(TSharedRef<SWidget> InWidget)
{
// This is a pretty dirty solution but it works.
// Since there is no way to access the actor picker's text block, we need to find it ourselves.
// We are going to recursively call this function until we find the text block, or run out of child widgets.
if (InWidget->GetType() == FName(TEXT("STextBlock"), FNAME_Find))
{
// The actor picker's text block is inside a horizontal box.
// This should be enough confirmation that this text block is the one we are looking for.
if (InWidget->GetParentWidget()->GetType() == FName(TEXT("SHorizontalBox"), FNAME_Find))
{
ActorPickerText = StaticCastSharedRef<STextBlock>(InWidget);
return true;
}
}
FChildren* Children = InWidget->GetChildren();
if (Children && Children->Num() > 0)
{
// Recursively go into child widgets.
for (int32 ChildrenIdx = 0; ChildrenIdx != Children->Num(); ++ChildrenIdx)
{
if (FindActorPickerTextBlock(Children->GetChildAt(ChildrenIdx)))
{
return true;
}
}
}
return false;
}
TSharedPtr<class SObjectPropertyEntryBox> ObjectPicker;
if (FindActorPickerTextBlock(ObjectPicker.ToSharedRef()))
{
// ActorPickerText->...
}
In the end I had to make it generic anyways, so here is that version as well.
/**
* Class for iterating through all childs of a widget, including childrens of child widgets.
* Iteration continues until the iterator func returns false, or we run out of widgets.
*/
class FChildWidgetIterator
{
public:
/**
* Callback function when iterating over a widget.
* Return false to stop the iterator.
*/
typedef TFunction<bool(SWidget&)> TWidgetIterationFunc;
/** Constructor. */
FChildWidgetIterator(SWidget& InParent, TWidgetIterationFunc InIterationFunc);
FChildWidgetIterator() = delete;
private:
/** Recursively iterate over all child widgets of the given widget. */
bool Advance(SWidget& InWidget);
/** Function to call whenever we iterate over a widget. */
TWidgetIterationFunc IterationFunc;
};
FChildWidgetIterator::FChildWidgetIterator(SWidget& InParent, TWidgetIterationFunc InIterationFunc)
{
IterationFunc = InIterationFunc;
Advance(InParent);
}
bool FChildWidgetIterator::Advance(SWidget& InWidget)
{
if (!IterationFunc(InWidget))
{
return false;
}
FChildren* Children = InWidget.GetChildren();
if (Children && Children->Num() > 0)
{
// Recursively go into child widgets.
for (int32 ChildrenIdx = 0; ChildrenIdx != Children->Num(); ++ChildrenIdx)
{
if (!Advance(*Children->GetChildAt(ChildrenIdx)))
{
return false;
}
}
}
return true;
}
// imagine this is a widget reference
TSharedPtr<class SCompoundWidget> WidgetToIterate;
FChildWidgetIterator WidgetIterator(*WidgetToIterate.Get(), [&](SWidget& InChild) -> bool
{
if (InChild.GetType() == FName(TEXT("STextBlock"), FNAME_Find))
{
STextBlock* TextWidget = static_cast<STextBlock*>(&InChild);
TextWidget->SetText(...);
return false; // stop the iterator
}
return true; // continue
});