Slate 表达式
关于 Slate 表达式
如果阅读过 Unreal 官方文档或 Unreal Editor 源代码,那么一定会见到类似这样的表达式:
SNew(SVerticalBox)
+ SVerticalBox::Slot().Padding(4.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().Padding(2.0f)
[
SNew(SButton).Text(FText::FromString(TEXT("BTN 01")))
]
+ SHorizontalBox::Slot().Padding(2.0f)
[
SNew(SButton).Text(FText::FromString(TEXT("BTN 02")))
]
]
+ SVerticalBox::Slot().Padding(4.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().Padding(2.0f)
[
SNew(SButton).Text(FText::FromString(TEXT("BTN 03")))
]
+ SHorizontalBox::Slot().Padding(2.0f)
[
SNew(SButton).Text(FText::FromString(TEXT("BTN 04")))
]
]
在本章节中,将以上这种用于描述 Slate 控件属性绑定和 Slate 控件间组合关系的特殊表达式称为 Slate 表达式。
Slate 表达式在 Unreal 官方文档中已有相关说明,但考虑到官方文档的一大特点是 “好像什么都讲了,但又好像什么都没有讲” ,本章将对 Slate 表达式的基本语法和常用范式做简单介绍。
实例化控件
Slate 表达式提供了一组用于实例化控件的宏,它们分别是:
SNew(WidgetType)
:实例化控件SAssignNew(ExposeAs, WidgetType)
:实例化控件,同时将控件引用赋给ExposeAs
SArgumentNew(InArgs, WidgetType)
:使用InArgs
作为参数实例化控件
这三个宏均使用 Unreal 智能引用实例化 Widget 。相较之下,SNew()
最为常用;SAssignNew()
在一些需要获取控件引用的场景下能减少代码量,增强易读性;SArgumentNew()
则提供了极高的自由度,可以用来构建复杂的自定义控件。
绑定 Attribute 属性
可以通过以下语法为 Attribute 属性绑定属性值:
SNew(WidgetType).AttrName(AttrValue)
Attribute 属性还支持绑定返回属性值的函数,可以使用以下语法绑定不同类型的函数:
SNew(WidgetType).AttrName_Static(Function) // Global Function
SNew(WidgetType).AttrName_Lambda([] { return AttrValue; }) // Lambda
SNew(WidgetType).AttrName_Raw(Object, Method) // Raw C++ Class Method
SNew(WidgetType).AttrName(ObjectRef, Method) // SP Class Method
SNew(WidgetType).AttrName_UObject(UObject, Method) // UObject Class Method
绑定 Argument 属性
可以通过以下语法为 Argument 属性绑定属性值:
SNew(WidgetType).ArgName(ArgValue)
Argument 属性不支持绑定函数,仅支持绑定值。
绑定 Event 属性
可以通过以下语法为 Event 属性绑定 Delegate 回调函数:
SNew(WidgetType).EventName(DelegateName::CreateStatic(Function))
SNew(WidgetType).EventName(DelegateName::CreateLambda([] { return AttrValue; }))
SNew(WidgetType).EventName(DelegateName::CreateRaw(Object, Method))
SNew(WidgetType).EventName(DelegateName::CreateSP(ObjectRef, Method))
SNew(WidgetType).EventName(DelegateName::CreateUObject(UObject, Method))
此外,Slate 核心库提供了一些语法糖,可以简化以上代码:
SNew(WidgetType).EventName_Static(Function) // Global Function
SNew(WidgetType).EventName_Lambda([] { return AttrValue; }) // Lambda
SNew(WidgetType).EventName_Raw(Object, Method) // Raw C++ Class Method
SNew(WidgetType).EventName(ObjectRef, Method) // SP Class Method
SNew(WidgetType).EventName_UObject(UObject, Method) // UObject Class Method
绑定 Slot 属性
在介绍控件基本类型时提到,派生自 SCompoundWidget
的 Slete 控件拥有 Slot 属性,可为其绑定 Slate 控件作为当前控件的子控件。在描述子控件时,使用 [...]
包裹子控件对应的 Slate 表达式。
Slot 属性有 Default Slot 和 Named Slot 两种。Default Slot 在绑定时可省略 Slot 名:
SNew(WidgetType)
// .DefaultSlotName()
[
SNew(...)
]
Named Slot 则必须在绑定时显式指定 Slot 名:
SNew(WidgetType)
.NamedSlotName()
[
SNew(...)
]
派生自 SCompoundWidget
的 Slate 控件通过 Slot 属性实现控件嵌套。由于每个 Slot 仅能容纳一个子控件,导致每种控件可容纳的子控件总数是确定的,这不便于实现一些布局类控件。
Slot()
方法
为解决 Slot 属性的局限性问题,派生自 SPanel
的 Slate 控件通过 Slot()
方法实现了在控件中包含多个并列结构的子控件。下面给出一个使用 Slot()
方法的例子:
SNew(WidgetType)
+ WidgetType::Slot().ArgName(ArgValue)
[
SNew(...)
]
+ WidgetType::Slot().ArgName(ArgValue)
[
SNew(...)
]
从以上 Slate 表达式中可以看出,Slate 核心库重写了 +
运算符,实现了将 [...]
内实例化的子控件通过 Slot()
方法作为参数传递给当前控件。
Slot()
方法也可绑定属性,但要注意,Slot()
方法的功能只是传递参数,并不实例化 Slate 控件。这里的 Slot()
属性与前文提到的控件属性有本质上的区别。在 Slate UI 中,拥有 Slot()
方法的 Slate 控件大多为布局类控件,相应地, Slot()
属性也大多与布局相关。