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.
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.
Thanks for this! It’s difficult to find simplified examples of UMG and Slate working together. Most folks suggest looking at widgets in the source code, but as someone who does not have a lot of experience reading code, it’s overwhelming to try to understand. This is a great and straightforward example