This is the final part of a mini-series on creating UIs in Unreal using UMG and Slate. Previously we discussed creating C++-based UserWidgets and creating new UMG classes.
Once again we will be implementing a simple button that contains an image and text, but this time we will see how to achieve the same result from Slate.
Note: This article links to the Epic Games Unreal Engine GitHub account for examples, to access it you will need to request permission.
Introduction
Slate is a UI framework that pre-dates UMG and Blueprints in Unreal. The Unreal editor itself is written using Slate.
When to use Slate, when to use UMG?
Some reasons for using Slate:
- Creating Editor UI: Custom asset inspectors, windows and other visual tools used to have to be implemented using Slate. As of 4.22, Editor Utility Widgets can be written using UMG+Blueprints, but as far as I know, asset inspectors must still be written in Slate.
- Need to implement low-level functionality that is not supported by any existing widgets. For example some complex graph-drawing might be better written in Slate than UMG C++.
It can be used to define UI layout in a somewhat unusual declarative syntax. Here's how we could declare a Button with an icon and text side-by-side:
SNew( SButton )
+ SButton::Slot()
[
SNew( SHorizontalBox )
+ SHorizontalBox::Slot()
.VAlign( VAlign_Center )
.HAlign( HAlign_Center )
[
SNew( SImage )
.Image( MyIconBrush )
]
+ SHorizontalBox::Slot()
.VAlign( VAlign_Center )
.HAlign( HAlign_Fill )
[
SNew( STextBlock )
.Text( FText::FromString( "Click me!" ) )
]
]
Hopefully your experience with creating UIs in the Blueprint editor and with UMG in C++ should make some of this familiar.
SButton
is the Slate button classSButton::Slot()
has properites the same as those you see in the Slot section of a Button` in the Blueprint editor.SHorizontalBox
is the Slate equivalent of UMG'sUHorizontalBox
.
In fact, SButton
is not the equivalent of UButton
, UButton
is a wrapper around SButton
!
class UButton : public UVisual
{
// ...
/** Cached pointer to the underlying slate
button owned by this UWidget */
TSharedPtr<SButton> MyButton;
};
Extending an Existing Slate Widget
I have found the easiest thing to do at first when learning Slate is to duplicate and extend an existing widget. For example let's create a SImage that supports multiple images, drawn on top of each other.
Find SImage
in the Unreal source code, and give it a read over.
Key functions:
Construct
is how the Slate widget is set up.SLATE_BEGIN_ARGS
inSImage.h
is where the arguments allowed by the widget are definedOnPaint
is where the widget defines how it is rendered.
Note: Make sure that you add Slate
to your project's dependencies in its Build.cs
file.
Creating a Slate Widget from Scratch
Hopefully extending an existing class should have given you a feel for Slate. Now it's time to create a new Slate widget from scratch.
You have a few choices for what to subclass:
SCompoundWidget
— this is one of the most commonly-used parent Slate classes.SLeafWidget
SPanel
ExampleSlate.h
#pragma once
#include "CoreMinimal.h"
class SExampleSlate : public SCompoundWidget
{
SLATE_BEGIN_ARGS(SExampleSlate){}
// See private declaration of OwnerHUD below.
SLATE_ARGUMENT(TWeakObjectPtr<class AHUD>, OwnerHUD)
SLATE_END_ARGS()
public:
// Required
void Construct(const FArguments& InArgs);
// Begin SWidget interface
void OnArrangeChildren(const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren) const override;
virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
// End SWidget interface
private:
TWeakObjectPtr<class AHUD> OwnerHUD;
};
ExampleSlate.cpp
In this example there isn't a lot of benefit from implementing the same behaviour in Slate over UMG. However for original, more complicated behaviour that cannot be broken down into pre-existing Slate widgets, creating a new Slate widget is your only option.