UE4的编辑器都是建立在Slate整个框架,包括UE4用于Runtime游戏的UMG这套也是基于Slate系统的。比如说,UButton这个Widget组件就封装了SButton, UButton各种控件事件都是来源于SButton。在Slate框架中,最基本的组件是SCompoundWidget.
UCLASS()
class UMG_API UButton : public UContentWidget
{
GENERATED_UCLASS_BODY()
xxxxxxx
xxxxxxx
protected:
/** Cached pointer to the underlying slate button owned by this UWidget */
TSharedPtr MyButton;
}
Widget Reflector在Windows->Developer Tools->Widget Reflector下。无论是使用Slate扩展UE4编辑器还是UMG构建游戏的UI,Widget Reflector都会经常性使用。
Widget Reflector用于定位一个UI控件种类和层级分布,比如我们可以定位UE4引擎的工具栏由哪些控件构成,并且层级分布如何
看看Content工具按钮所在的UI层级:
SCompoundWidget是我们构建组件组件的基本单位, 像SImage,SButton,SBorder, SOverlay等等S开头的控件都是SCompoundWidget的子类。下面说说SCompoundWidget的基本构造
class SMyCompoundWidget : public SCompoundWidget
{
};
在继承SCompoundWidget写我们的自定义组件,经常会出现SCompoundWidget的内置函数爆红线提醒,无法正确看到函数的注释的问题,如下所示:
得额外包含 #include "Widgets/DeclarativeSyntaxSupport.h" 解决这个问题
SCompoundWidget的参数是外部传入的,用于改变SCompoundWidget行为(比如颜色,点击事件,控件大小,控件布局等)的参数,看下面这段代码:
// The delete button for removing blocks is only visible when in edit mode
SNew( SButton )
.Visibility( this, &SMultiBoxWidget::GetCustomizationVisibility, BlockWeakPtr, BlockWidgetWeakPtr )
.ContentPadding(0)
.OnClicked( this, &SMultiBoxWidget::OnDeleteBlockClicked, BlockWeakPtr )
.ButtonStyle( StyleSet, "MultiBox.DeleteButton" )
像Visibility指明SButton对象的可见性,ContentPadding指明了内容填充大小,OnClicked为按钮的绑定事件,ButtonStyle指明了按钮的外表样式。这些都是SButton控件的参数。
在宏 “SLATE_BEGIN_ARGS” 和 “SLATE_END_ARGS”之间的 "{}"中声明参数. SCompoundWidget的常用参数类型有
SLATE_ATTRIBUTE(属性), SLATE_EVENT(事件),SLATE_ARGUMENT(参数), SLATE_NAMED_SLOT(插槽) 和SLATE_DEFAULT_SLOT.
下面具体介绍SLATE_ATTRIBUTE和SLATE_EVENT的使用.
SLATE_ATTRIBUTE
声明格式: SLATE_ATTRIBUTE(变量类型,变量名)
这里的变量类型为常见的变量类型,如FString, float, FText, FMargin,int,FVector2D,FSlateColor等等这些普通变量类型。
例子:
SLATE_ATTRIBUTE( FText, Text ),
SLATE_ATTRIBUTE( FSlateColor, ForegroundColor )
SLATE_EVENT
声明格式: SLATE_EVENT(变量类型,变量名)
这里的变量类型为UE4的委托类型,比如DECLARE_DELEGATE( FSimpleDelegate ),
,DECLARE_DELEGATE_RetVal(FReply, FOnClicked )
例子:
DECLARE_DELEGATE(FSimpleDelegate)
SLATE_EVENT( FSimpleDelegate, OnPressed)
总上所述:SCompoundWidget的参数声明例子:
#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
DECLARE_DELEGATE_OneParam(FMyEvent, FString);
/**
*
*/
class EXSLATEWIDGET_API SMyCompoundWidget : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SMyCompoundWidget)
{
}
SLATE_ATTRIBUTE(FSlateColor, MySlateColor)
SLATE_EVENT(FMyEvent, MyEvent)
SLATE_END_ARGS()
};
SCompoundWidget的参数初始化
按上面用相应的宏声明之后,实际上参数名变为"_" + "宏里面的变量名',如上面 “MySlateColor” 参数名为“_MySlateColor”.SCompoundWidget的参数初始化和C++类差不多,如同下面:
SLATE_BEGIN_ARGS(SMyCompoundWidget)
{
_MySlateColor = FLinearColor(1.0f, 1.0f, 1.0f, 1.0f);
}
或者
SLATE_BEGIN_ARGS(SMyCompoundWidget):
_MySlateColor(FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))
{
}
SCompoundWidget的Event参数和类成员委托变量之间的转移(变量和参数的区分)
我们在创建SCompoundWidget对象时传入的事件委托不是直接通过上面我们定义的那些参数直接执行的,上面定义的那些参数是为了方便传递,看下面
// The delete button for removing blocks is only visible when in edit mode
SNew( SButton )
.Visibility( this, &SMultiBoxWidget::GetCustomizationVisibility, BlockWeakPtr, BlockWidgetWeakPtr )
.ContentPadding(0)
.OnClicked( this, &SMultiBoxWidget::OnDeleteBlockClicked, BlockWeakPtr )
.ButtonStyle( StyleSet, "MultiBox.DeleteButton" )
传递的Event参数,在SCompoundWidget内部赋予给类内的委托变量,SCompoundWidget存在一个Construct构造函数, 其执行在上面的声明初始化参数阶段之后
DECLARE_DELEGATE_OneParam(FMyEvent, FString);
class EXSLATEWIDGET_API SMyCompoundWidget : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SMyCompoundWidget) :
_MySlateColor(FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))
{
}
SLATE_ATTRIBUTE(FSlateColor, MySlateColor)
SLATE_EVENT(FMyEvent, MyEvent)
SLATE_END_ARGS()
public:
void Construct(const FArguments& InArgs);
private:
FMyEvent MyEvent;
};
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SMyCompoundWidget::Construct(const FArguments& InArgs)
{
MyEvent = InArgs._MyEvent;
/*
ChildSlot
[
// Populate the widget
];
*/
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
可以看到上面的“InArgs._MyEvent”是SNew(SMyCompoundWidget)传入的参数, 例子如下(伪代码):
class A
{
public:
void PrinString(FString aaa)
{
XXXXXXXXX
XXXXXXXXX
}
void BeginPlay()
{
SNew(SMyCompoundWidget)
.MyEvent(this, &A::PrinString)
.MySlateColor(FLinearColor(1.0f, 0.0f, 0.0f, 1.0f))
}
}
创建SCompoundWidget对象,可以使用SNew或者SAssignNew,如下所示:
TSharedPtr MyButtonObject = SNew(SButton);
TSharedRef MyButtonRef;
SAssignNew(MyButtonRef, SButton);
之前在(UE4 4.20 )UE4的GC(垃圾回收)编程规范说过关于TSharedRef和TSharedPtr的概念
由于SCompoundWidget都继承于SWidget, 而SWidget继承TSharedFromThis
TSharedPtr MyButtonObject = SNew(SButton);
MyButtonRef = MyButtonObject->AsShared();
或者
TSharedPtr MyButtonObject = SNew(SButton);
MyButtonRef = MyButtonObject.ToSharedRef();
如果你改写SCompoundWidget的Construct函数额外添加参数,则SNew创建对象的时候,可以对类进行传参
class EXSLATEWIDGET_API SMyCompoundWidget : public SCompoundWidget
{
public:
void Construct(const FArguments& InArgs, int a)
{
}
};
TSharedPtr MyCompoundWidget;
MyCompoundWidget = SNew(SMyCompoundWidget, 5);
在UE4中的UMG中,UUserWidget可以组合控件,Button,Image, VerticalBox. 如下:
我们在SCompoundWidget也是这样的方式进行组合,不过对应的控件是以S开头的控件,如SButton, SImage,SVerticalBox。
在SCompoundWidget的Construct函数中的ChildSlot组合各种控件
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SMyCompoundWidget::Construct(const FArguments& InArgs)
{
MyEvent = InArgs._MyEvent;
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
SNew(SButton)
[
SNew(SImage)
]
]
];
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
SCompoundWidget通过Slot(插槽)嵌套子SCompoundWidget,如上面例子所示,不过UE4内部并非每个SCompoundWidget都具备Slot, 比如SImage和STextBlock不存在Slot。 并且就算具备Slot,可能只能嵌套固定数量的Slot, 比如SButton, 也可能可以嵌套动态数量的Slot,比如SVerticalBox和SHorizontalBox (和UMG系统类似的)
按照所有所描述的分类,分为三种:
(1)Leaf Widgets(叶子控件) : 不具备slot插槽,不能嵌套子SCompoundWidget,如SImage,STextBlock, 这些都继承于SLeafWidget.
SNew(SImage)
(2)Panels (面板): 动态添加Slot来嵌套N多个子SCompoundWidget, 如上面的SVerticalBox, SHorizontalBox ,SOverlay等等,面板继承于SPanel。
一般而言这类控件都定义了一个Slot()的静态函数
嵌套方式:
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SButton)
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SButton)
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SButton)
]
];
当然也可以通过AddSlot或者InsertSlot来添加子Slot
TSharedPtr MyVerticalBox;
MyVerticalBox = SNew(SVerticalBox);
MyVerticalBox->AddSlot()
[
SNew(SButton)
];
MyVerticalBox->AddSlot()
[
SNew(SButton)
];
MyVerticalBox->AddSlot()
[
SNew(SButton)
];
ChildSlot
[
MyVerticalBox->AsShared()
];
(3)Compound Widgets (混合控件):容纳固定数量的Slot,所以只能嵌套固定数量的 子SCompoundWidget,, 如SButton就只能嵌套一个子SCompoundWidget
嵌套方式:
SNew(SButton)
[
SNew(SImage)
]
这里大概讲的是SLATE_ATTRIBUTE 和 SLATE_EVENT的赋值
SLATE_ATTRIBUTE:
(1)直接赋值
(2)绑定返回值委托赋值, 可以动态改变返回值
直接赋值
SLATE_ATTRIBUTE(FLinearColor, MySlateColor)
SNew(SMyCompoundWidget)
.MySlateColor(FLinearColor(1.0f, 1.0f, 1.0f,1.0f))
或者
动态绑定
SNew(SMyCompoundWidget)
.MySlateColor(this, &ThisClass::GetColor)
FLinearColor GetColor() const
{
return FLinearColor(1.0f, 1.0f, 1.0f, 1.0f);
}
SLATE_EVENT
直接绑定对应类型的委托
DECLARE_MULTICAST_DELEGATE_OneParam(FMyDelagate, FString);
SLATE_EVENT(FMyDelagate, MyEvent)
SNew(SMyCompoundWidget)
.MyEvent(this, &ThisClass::OnClick)
void OnMyEvent(FString Str)
{
// do something
return FReply::Handled();
}
上面两种属性都可以绑定于委托,而委托函数有多种情况,所以有些情况你得使用委托的Raw,Static等等
SNew(SButton)
//.OnClicked
//.OnClicked_Raw
//.OnClicked_Static
//.OnClicked_Static
//.OnClicked_Lambda
具体委托相关参考博客(UE4 4.20)UE4的委托(Delegate)使用
[1]Slate 用户界面框架