UE4 Slate Widget Example

I am starting a series of posts dedicated to UE4 Slate UI Framework. There will be tutorials, examples and quick how-tos. Everything to cover a lack of documentation on the topic from Epics. I’ll start with a simple example of a basic UE4 Slate widget where you select a value from a dropdown, enter a text and press an action button.

UE4 Slate Example markup
Slate UI markup

To start, create a C++ project. Create a subclass of UWidget: UGreetingsWidget. This will be a wrapper for our Slate widget:

#pragma once

#include "CoreMinimal.h"

#include "Components/Widget.h"

#include "GreetingsWidget.generated.h"

/**
 * 
 */
UCLASS()
class SLATEEXAMPLE_API UGreetingsWidget : public UWidget
{
	GENERATED_BODY()

protected:
	virtual TSharedRef<SWidget> RebuildWidget() override;
	
public:
	UPROPERTY(EditAnywhere, Category=Superyateam)
	FSlateBrush Background;
	
	virtual const FText GetPaletteCategory() override;
};

And the implementation:

#include "GreetingsWidget.h"

#include "SGreetings.h"

#define LOCTEXT_NAMESPACE "UGreetingsWidget"

TSharedRef<SWidget> UGreetingsWidget::RebuildWidget()
{
	return SNew(SGreetings)
	.Background(&Background);
}

const FText UGreetingsWidget::GetPaletteCategory()
{
	return LOCTEXT("PaletteCategory", "Superyateam");
}

#undef LOCTEXT_NAMESPACE

You see RebuildWidget method? It will create a widget and it uses Slate UI framework for that. All UMG widgets in UE4 use Slate UI framework under the hood.

Now comes the Slate widget. Create a C++ class SGreetings which extends SCompoundWidget. This is going to be our Slate UI base class:

#pragma once

#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"

class SEditableTextBox;
/**
 * 
 */
class SLATEEXAMPLE_API SGreetings : public SCompoundWidget
{
	static const TArray<FText> Titles;

	TSharedPtr<STextBlock> GreetBox;
	TSharedPtr<SEditableTextBox> NameBox;
	TSharedPtr<FText> SelectedTitle;
	TArray<TSharedPtr<FText>> TitleOptions;
	
public:
	SLATE_BEGIN_ARGS(SGreetings) {}
		SLATE_ARGUMENT(FSlateBrush*, Background)
	SLATE_END_ARGS()

	/** Constructs this widget with InArgs */
	void Construct(const FArguments& InArgs);
	FReply Greet();
};

And the implementation:

#include "SGreetings.h"
#include "SlateOptMacros.h"
#include "Widgets/Input/SEditableTextBox.h"

#define LOCTEXT_NAMESPACE "SGreetings"

const TArray<FText> SGreetings::Titles =
	{
		LOCTEXT("Mr", "Mr."),
		LOCTEXT("Mrs", "Mrs."),
		LOCTEXT("Ms", "Ms."),
	};

BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SGreetings::Construct(const FArguments& InArgs)
{
	SelectedTitle = MakeShareable(new FText(Titles[0]));
	for (const auto& Title : Titles)
	{
		TitleOptions.Add(MakeShareable(new FText(Title)));
	}
	
	TSharedPtr<SComboBox<TSharedPtr<FText>>> TitleComboBox;
	ChildSlot
	[
		SNew(SBorder)
		.BorderImage(InArgs._Background)
		.Padding(FMargin(20))
		[
			SNew(SVerticalBox)
			+SVerticalBox::Slot()
			.AutoHeight()
			[
				SNew(SHorizontalBox)
				+SHorizontalBox::Slot()
				[
					SAssignNew(TitleComboBox, SComboBox<TSharedPtr<FText>>)
					.InitiallySelectedItem(SelectedTitle)
					.OptionsSource(&TitleOptions)
					.OnSelectionChanged_Lambda([this](TSharedPtr<FText> NewSelection, ESelectInfo::Type SelectInfo)
					{
						SelectedTitle = NewSelection;
					})
					.OnGenerateWidget_Lambda([](TSharedPtr<FText> Option)
					{
						return SNew(STextBlock)
						.Font(FCoreStyle::GetDefaultFontStyle("Regular", 18))
						.Text(*Option);
					})
					[
						SNew(STextBlock)
						.Font(FCoreStyle::GetDefaultFontStyle("Regular", 18))
						.Text_Lambda([this]()
						{
							return SelectedTitle.IsValid() ? *SelectedTitle: FText::GetEmpty();
						})
					]
				]
				+SHorizontalBox::Slot()
				.Padding(5, 0)
				[
					SAssignNew(NameBox, SEditableTextBox)
					.Font(FCoreStyle::GetDefaultFontStyle("Regular", 18))
					.HintText(LOCTEXT("YourName", "Fill your name"))
				]
				+SHorizontalBox::Slot()
				.Padding(5, 0)
				[
					SNew(SButton)
					.OnClicked(this, &SGreetings::Greet)
					[
						SNew(STextBlock)
						.Font(FCoreStyle::GetDefaultFontStyle("Regular", 18))
						.Text(LOCTEXT("Hello", "Hello!"))
					]
				]
			]
			+SVerticalBox::Slot()
			.Padding(0, 10)
			[
				SAssignNew(GreetBox, STextBlock)
				.Font(FCoreStyle::GetDefaultFontStyle("Regular", 18))
			]
		]
	];
}

FReply SGreetings::Greet()
{
	GreetBox->SetText(FText::Format(LOCTEXT("GreetingsText", "Hello, {0} {1}!"), *SelectedTitle, NameBox->GetText()));
	return FReply::Handled();
}

END_SLATE_FUNCTION_BUILD_OPTIMIZATION

#undef LOCTEXT_NAMESPACE

For better readability I pasted this code into a gist. You are welcome!

Now, the C++ part is done, let’s see how it looks in Unreal Editor.

The widget appears under Superyateam category. We did override GetPaletteCategory method to make this happen. We also defined Background brush with RGBA(0, 0, 0, 0.4) value to make a semi-transparent background. We declared that property earlier in UGreetingsWidget class, remember?

Now, in action:

That’s it. One last thing I want to tell you is to have a look at SWidgetGallery.cpp file in the Engine source. It might be a good source for UE4 slate example widgets.


Hey, folks! I would really appreciate if in comments you tell all the areas of Slate UI framework which appear problematic for you. I reply right in the comments for easy ones and create new posts in the series for difficult ones.

Leave a Reply

Your email address will not be published. Required fields are marked *