Slate语法

大概类型关系图
C++中使用时需要包含Slate.h头文件。参考Project Setup

The square brackets define what elements are nested inside a given widget, while periods set the value of the parameters you would see in the Details panel. 参考
加号则用于SXXXWidget::Slot()的添加,返回的是FArguments类型。而SXXXWidget类型里通过SLATE_BEGIN_ARGS宏来定义了FArguments结构体类型,并用SLATE_ATTRIBUTE、SLATE_ARGUMENT等宏来给FArguments类型添加一些属性。

 对SNew或SAssignNew的理解
实际上,SNew或SAssignNew的宏展开可以简略地视为:MakeTDecl<WidgetType>() <<= WidgetType::FArguments()
这里的MakeTDecl模板函数返回的是一个TSlateDecl<WidgetType, xxx>类型的对象。
由于<<运算符的优先级比较低,所以上面宏展开里的WidgetType::FArguments()先和SNew(WidgetType)后面的语句结合,结果就是构造了WidgetType::FArguments对象并对其属性进行了赋值
即<<=语句的右边是一个WidgetType::FArguments对象,左边是一个TSlateDecl<WidgetType, xxx>对象,而该TSlateDecl对象又重载了<<=运算符,因此对该对象调用其<<=运算,其接受WidgetType::FArguments对象作为运算参数,最终返回一个TSharedRef<WidgetType>对象;
因此SNew(WidgetType)及其紧跟的代码块最终是构造了一个WidgetType对象;
以SOverlay为例,具体示例可以在SScrollBox::ConstructVerticalLayout()函数里找到。

结合上面两点,通常+号用于增加Slots,Slots后面的(.)点号与Slot先结合,所以紧跟Slots的(.)点号设置的是Slot的属性,而紧跟SNew的(.)点号设置的是WidgetType::FArguments对象的属性

 对TAttribute属性的赋值
官网例子来说,SNew(SCheckBox).IsChecked(这里需要ECheckBoxState型参数),IsChecked就是一个attribute,赋值如下:.IsChecked(TAttribute<ECheckBoxState>(this, &SQuickStartWindowMenu::IsTestBoxChecked)),更详细的信息参见How to set a slate attribute dynamically?

 Padding
The padding of a widget amount of spacing in slate units around the left, top, right, and bottom parts of the widget within its parent. 离父widget的边框的空白,在父边框之内为正值。父边框是指父widget的content区域的范围。参考css box model CSS Box Model

 Placemode里添加Category
FPlacementModeModule用于提供这个功能。参考FBspModeModule::StartupModule()的实现。

CSS Box Model

Explanation of the different parts:
The box model allows us to add a border around elements, and to define space between elements.

关于Unreal Engine中Widget的Slot

各种Layouts与Widgets之间可能会有嵌套、组合之类的关系的。
比如,一个Layout引用一个widget,通常C++里可能会引用widget基类指针,UE4对这种使用指针的用法进行了建模,用Slot来代替指针的作用,Slot里比普通的指针提供了额外的一些信息,可以认为是一个强化版的指针,它包含双指针,一个指向这个Slot对应的Widget(C),一个指向该Widget(C)的ParentWidget(通常值就是this)。因此,每一个Widget里都有一个Slot成员(定义在Widget.h里的UPanelSlot* Slot;)。
阅读UPanelSlot* UPanelWidget::AddChild(UWidget* Content)函数源码,如下:
 UPanelSlot* PanelSlot = NewObject<UPanelSlot>(this, GetSlotClass(), NAME_None, NewObjectFlags);
 PanelSlot->Content = Content;
 PanelSlot->Parent = this;
 Content->Slot = PanelSlot;
这里能够清晰看到,Content作为一个UWidget,其Slot成员的Content属性指向该Widget自身,Parent属性指向该Widget的父Widget。即我们应该关注的是Slot树状结构,这棵树才是我们看到的UI布局的逻辑结构,树的节点都是Slot,每个Slot都有Slot->Content访问的是真正的Widget,Slot->Parent访问的是Widget的ParentWidget,同时注意,Parent类型是UPanelWidget*,还有Slot要想访问其父Slot,应该通过Slot->Parent->Slot
注意派生自UWidget的UPanelWidget体内增加了TArray<UPanelSlot*> Slots数据成员