home

Tutorial index

Unreal Engine 4: Slate UI Tutorial 3: nested components

In this tutorial we will look at how you can place more than one components on an overlay, and how to nest the components to organize your UI more efficiently. Furthermore, a complete list of the slate components provided by the engine can be found at the bottom of the page, with a brief description of each one.

Disclaimer: information provided in this tutorial on positioning of widgets and the overal behavior of the slate system is the result of observations and experimentations.

To begin with, to nest widgets you need a widget that can house other widgets within it. SHorizontalBox and SVerticalBox are two such widgets which you are likely to use a lot. They create an imaginary box in which widgets are added in a horizontal or vertical alignment. You could nest multiple SHorizontalBoxes in an SVerticalBox or vice-versa to create a grid. Let's go to the point where we started adding widgets and create a vertical box:

ChildSlot.VAlign(VAlign_Fill).HAlign(HAlign_Fill)
[
    SNew(SVerticalBox)
    + SVerticalBox::Slot()
    .HAlign(HAlign_Left)
    .VAlign(VAlign_Center)
    [
    ]
];

This vertical box is the root component (I can't guarantee that is the official terminology). To add components within it, we add slots, taken from the SVerticalBox namespace. The first slot we create is alligned to the left horizontally, meaning it will try to stay on the left side as other widgets are added on the same level as itself, and on the center vertically, equivalently meaning it will try to remain on the center between other widgets in the same parent container. Within the brackets [] we can add as many other components as we wish. Let's populate this slot with a textbox, let's assume this to be the pause menu, so we'll add an appropriate text. Formatting and styling are covered in the next tutorials, so don't worry if the text is too small.


SNew(SVerticalBox)
+ SVerticalBox::Slot()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
     SNew(STextBlock)
    .ShadowColorAndOpacity(FLinearColor::Black)
    .ColorAndOpacity(FLinearColor::Red)
    .ShadowOffset(FIntPoint(-1, 1))
    .Font(FSlateFontInfo("Verdana", 16))
    .Text(FString("Pause Menu"))
]

To be honest, I just picked up the textbox from the previous tutorial and dropped it in, placing the right text in the output. So now you've got some text, but that doesn't really demonstrate much on nesting widgets. So let's put an SHorizontalBox inside that:


SNew(SVerticalBox)
+ SVerticalBox::Slot()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
     SNew(STextBlock)
     .ShadowColorAndOpacity(FLinearColor::Black)
     .ColorAndOpacity(FLinearColor::Red)
     .ShadowOffset(FIntPoint(-1, 1))
     .Font(FSlateFontInfo("Verdana", 16))
     .Text("Pause Menu")
]
+ SVerticalBox::Slot()
.VAlign(VAlign_Center)
[
     SNew(SHorizontalBox)
     + SHorizontalBox::Slot()
     .HAlign(HAlign_Left)
     .VAlign(VAlign_Top)
     [
          SNew(STextBlock)
          .ShadowColorAndOpacity(FLinearColor::Black)
         .ColorAndOpacity(FLinearColor::Red)
         .ShadowOffset(FIntPoint(-1, 1))
         .Font(FSlateFontInfo("Verdana", 16))
         .Text(FString("Options:"))
     ]
]

There! Ok, but what do we expect to see?

Screen space distribution:

Slate doesn't exactly clear up where things will wind up being. The important thing to remember is that things are nested, meaning that once the distribution of the outer items is done, the inner items are distributed within their parents as if their parents were the entire screen. This does not mean that the parent will not grow to accomodate more data, but rather that you will have the hierarchy treated with respect. Let's look at the case above. You have a vertical box, which is to be in the center-left. It is however the only widget at its level, meaning that it may well expand to take up the entire screen, though it will prefer the center left part. Within it, you have 1) a text block and 2) a horizontal box. These have to share the space of their parent, the parent dictating that they will be distributed vertically. This means that all components are stacked one below the other, in the order given. These nested widgets' alignment concerns the area allocated to them, not their position in their parent area, which is determined, as stated just now, by the order they are added to it. Finally the new text block in the horizontal box is placed in the area of the horizontal box, rather than as a third element in the vertical box. To make things more apparent, copy-paste pieces of code to create larger UI structures:


SNew(SVerticalBox)
+ SVerticalBox::Slot()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
     SNew(STextBlock)
     .ShadowColorAndOpacity(FLinearColor::Black)
     .ColorAndOpacity(FLinearColor::Red)
     .ShadowOffset(FIntPoint(-1, 1))
     .Font(FSlateFontInfo("Verdana", 16))
     .Text("Pause Menu")
]
+ SVerticalBox::Slot()
.VAlign(VAlign_Center)
[
     SNew(SHorizontalBox)
     + SHorizontalBox::Slot()
     .HAlign(HAlign_Left)
     .VAlign(VAlign_Top)
     [
          SNew(STextBlock)
          .ShadowColorAndOpacity(FLinearColor::Black)
         .ColorAndOpacity(FLinearColor::Red)
         .ShadowOffset(FIntPoint(-1, 1))
         .Font(FSlateFontInfo("Verdana", 16))
         .Text(FString("Options:"))

          SNew(STextBlock)
          .ShadowColorAndOpacity(FLinearColor::Black)
         .ColorAndOpacity(FLinearColor::Red)
         .ShadowOffset(FIntPoint(-1, 1))
         .Font(FSlateFontInfo("Verdana", 16))
         .Text(FString("Game:"))

          SNew(STextBlock)
          .ShadowColorAndOpacity(FLinearColor::Black)
         .ColorAndOpacity(FLinearColor::Red)
         .ShadowOffset(FIntPoint(-1, 1))
         .Font(FSlateFontInfo("Verdana", 16))
         .Text(FString("Info:"))
     ]
+ SVerticalBox::Slot()
.VAlign(VAlign_Center)
[
     SNew(SHorizontalBox)
     + SHorizontalBox::Slot()
     .HAlign(HAlign_Left)
     .VAlign(VAlign_Top)
     [
          SNew(STextBlock)
          .ShadowColorAndOpacity(FLinearColor::Black)
         .ColorAndOpacity(FLinearColor::Red)
         .ShadowOffset(FIntPoint(-1, 1))
         .Font(FSlateFontInfo("Verdana", 16))
         .Text(FString("Options2:"))

          SNew(STextBlock)
          .ShadowColorAndOpacity(FLinearColor::Black)
         .ColorAndOpacity(FLinearColor::Red)
         .ShadowOffset(FIntPoint(-1, 1))
         .Font(FSlateFontInfo("Verdana", 16))
         .Text(FString("Game2:"))

          SNew(STextBlock)
          .ShadowColorAndOpacity(FLinearColor::Black)
         .ColorAndOpacity(FLinearColor::Red)
         .ShadowOffset(FIntPoint(-1, 1))
         .Font(FSlateFontInfo("Verdana", 16))
         .Text(FString("Info2:"))
     ]
]

Try using such a code and see what happens! See if you can predict where each item will go by changing their text to "Center-left", "Lower-right" and so on. Practice makes perfect!

 

Below follows a list of basic slate components supported by Engine Version 4.8.3:

SBorder

SBox

SBoxPanel

SBreadcrumTrail

SBuuildProgressWidget

SButton

SCanvas

SCheckBox

SComboBox

SComboButton

SComboRow

SColorBlock

SColorSpectrum

SColorWheel

SCompoundWidget

SDPIScaler

SEditableComboBox

SEditableText

SEditableTextBox

SErrorHint

SErrorText

SExpandableArea

SExpandableButton

SExpanderArrow

SFxWidget

SGraphNode

SGridPanel

SHeader

SHeaderRow

SHorizontalBox

SHyperlink

SImage

SInlineEditableTextBlock

SLeafWidget

SListPanel

SListView

SMenuAnchor

SMissingWidget

SMultiBlockBaseWidget

SMultiBoxWidget

SMultiColumnTableRow

SMultiLineEditableText

SMultiLineEditableTextBox

SNotificationItem

SNotificationList

SNulWidget

SNumericEntryBox

SOverlay

SPanel

SPopup

SPopupErrorText

SPopupLayer

SProgressBar

SRichTextBlock

SRichTextHyperlink

SRotatorInputBox

SSafezone

SScrollBar

SScrollBarTrack

SScrollBorder

SScrollBox

SSearchBox

SSeparator

SSlider

SSpacer

SSpinBox

SSpinningImage

SSpliter

SSpliter2x2

SSuggestionTextBox

STableRow

STableViewBase

STextBlock

STextComboBox

STextComboPopup

STextEntryPopup