Find child slate widget by type in C++

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->...
}
1 Like

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
});