一、概述
UE4的大部分编辑器界面都是Slate构建的,包括一些插件也是由Slate构建。Slate是一个分辨率自适应的相对布局界面系统,为了完成这样的目的,slate实际上采用了一个两次排布的“思路”。
(一)、首先是递归计算每个控件的大小,父控件会根据子控件来计算自己的大小。
(二)、根据控件大小,计算出每个控件的绘制位置。
二、Slate渲染
所有的Slate对象首先会准备渲染的内容,即WindowElement,然后这些内容会交给SlateRHIRenderer负责,最终被绘制出来。
渲染流程为:
(一)、将控件对象转换为需要绘制的图形面片。
(二)、通过PixelShader和VertexShader来使用GPU绘制。
(三)、拿回绘图结果,显示在SWindow中。
值得注意的是:Slate的渲染是没有开启深度检测的。
三、Slate的继承方式
所有的Slate部件都继承自SWidget,SWidget继承自FSlateControlledConstruction和TSharedFromThis。SWidget分为SCompoundWidget、SEditableText、SLeafWidget、SMultiLineEditableText、SPanel、SRichTextBlock、SWeakWidget。
SCompoundWidget只含有一个FSimpleSlot 类型的ChildSlot,只可以添加一个Child Widget,在Construct()函数里面初始化ChildSlot,简单的布局。例如SBorder、SButton。
SEditableText、SLeafWidget、SMultiLineEditableText、SRichTextBlock不包含ChildSlot,不可以添加Widget。
SPanel可以包含多个ChildWidget,Spanel本身没有Slot结构,继承它的子类自己添加Slot结构,并且实现不同的结构方式。例如SWidgetSwitcher。
不同的继承方式,ChildSlot也不同。
四、Slate属性方法
Slate提供了一系列宏来初始化控件。
在SLATE_BEGIN_ARGS()和SLATE_END_ARGS()之间声明参数。
常用的宏有SLATE_ATTRIBUTE(属性)、SLATE_EVENT(事件)、SLATE_ARGUMENT(参数)、SLATE_NAMED_SLOT(插槽) 和 SLATE_DEFAULT_SLOT。
SLATE_BEGIN_ARGS(SBackgroundBlurWidget)
: _HAlign(HAlign_Fill)
, _VAlign(VAlign_Fill)
, _Padding(FMargin(2.0f))
, _bApplyAlphaToBlur(true)
, _BlurStrength(0.f)
, _BlurRadius()
, _LowQualityFallbackBrush(nullptr)
{
_Visibility = EVisibility::SelfHitTestInvisible;
}
SLATE_DEFAULT_SLOT(FArguments, Content)
SLATE_ARGUMENT(EHorizontalAlignment, HAlign)
SLATE_ARGUMENT(EVerticalAlignment, VAlign)
SLATE_ATTRIBUTE(FMargin, Padding)
SLATE_ARGUMENT(bool, bApplyAlphaToBlur)
SLATE_ATTRIBUTE(float, BlurStrength)
SLATE_ATTRIBUTE(TOptional<int32>, BlurRadius)
SLATE_ARGUMENT(const FSlateBrush*, LowQualityFallbackBrush)
SLATE_END_ARGS()
构造函数初始化
void SBackgroundBlurWidget::Construct(const FArguments& InArgs)
{
bApplyAlphaToBlur = InArgs._bApplyAlphaToBlur;
LowQualityFallbackBrush = InArgs._LowQualityFallbackBrush;
BlurStrength = InArgs._BlurStrength;
BlurRadius = InArgs._BlurRadius;
ChildSlot
.HAlign(InArgs._HAlign)
.VAlign(InArgs._VAlign)
.Padding(InArgs._Padding)
[
InArgs._Content.Widget
];
绘制函数:SLeafWidget和SCompoundWidget是在OnPaint中用FSlateDrawElement绘制。
virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
nt32 SBackgroundBlurWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
int32 PostFXLayerId = LayerId;
if (bAllowBackgroundBlur && AllottedGeometry.GetLocalSize() > FVector2D::ZeroVector)
{
if (!bForceLowQualityBrushFallback)
{
// Modulate blur strength by the widget alpha
const float Strength = BlurStrength.Get() * (bApplyAlphaToBlur ? InWidgetStyle.GetColorAndOpacityTint().A : 1.f);
if (Strength > 0.f)
{
FPaintGeometry PaintGeometry = AllottedGeometry.ToPaintGeometry();
// extract the layout transform from the draw element
FSlateLayoutTransform InverseLayoutTransform(Inverse(FSlateLayoutTransform(PaintGeometry.DrawScale, PaintGeometry.DrawPosition)));
// The clip rect is NOT subject to the rotations specified by MakeRotatedBox.
FSlateRotatedClipRectType RenderClipRect = FSlateRotatedClipRectType::MakeSnappedRotatedRect(MyClippingRect, InverseLayoutTransform, AllottedGeometry.GetAccumulatedRenderTransform());
float OffsetX = PaintGeometry.DrawPosition.X - FMath::TruncToFloat(PaintGeometry.DrawPosition.X);
float OffsetY = PaintGeometry.DrawPosition.Y - FMath::TruncToFloat(PaintGeometry.DrawPosition.Y);
int32 RenderTargetWidth = FMath::RoundToInt(RenderClipRect.ExtentX.X);
int32 RenderTargetHeight = FMath::RoundToInt(RenderClipRect.ExtentY.Y);
int32 KernelSize = 0;
int32 DownsampleAmount = 0;
ComputeEffectiveKernelSize(Strength, KernelSize, DownsampleAmount);
float ComputedStrength = FMath::Max(.5f, Strength);
if (DownsampleAmount > 0)
{
RenderTargetWidth = FMath::DivideAndRoundUp(RenderTargetWidth, DownsampleAmount);
RenderTargetHeight = FMath::DivideAndRoundUp(RenderTargetHeight, DownsampleAmount);
ComputedStrength /= DownsampleAmount;
}
if (RenderTargetWidth > 0 && RenderTargetHeight > 0)
{
// FSlateDrawElement::MakePostProcessPass(OutDrawElements, LayerId, PaintGeometry, MyClippingRect, FVector4(KernelSize, ComputedStrength, RenderTargetWidth, RenderTargetHeight), DownsampleAmount);
}
++PostFXLayerId;
}
}
else if (bAllowBackgroundBlur && bForceLowQualityBrushFallback && LowQualityFallbackBrush && LowQualityFallbackBrush->DrawAs != ESlateBrushDrawType::NoDrawType)
{
const bool bIsEnabled = ShouldBeEnabled(bParentEnabled);
const uint32 DrawEffects = bIsEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;
const FLinearColor FinalColorAndOpacity(InWidgetStyle.GetColorAndOpacityTint() * LowQualityFallbackBrush->GetTint(InWidgetStyle));
FSlateDrawElement::MakeBox(OutDrawElements, PostFXLayerId, AllottedGeometry.ToPaintGeometry(), LowQualityFallbackBrush, MyClippingRect, DrawEffects, FinalColorAndOpacity);
++PostFXLayerId;
}
}
return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, PostFXLayerId, InWidgetStyle, bParentEnabled);
}
数据更新
UE4提供了函数SynchronizeProperties(),编译时会调用该函数,使得属性能够同步到UI控件上。
PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)该函数响应编辑器属性改变事件。
Slate代理
DECLARE_DYNAMIC_DELEGATE_RetVal(bool, FGetBool);
DECLARE_DYNAMIC_DELEGATE_RetVal(float, FGetFloat);
DECLARE_DYNAMIC_DELEGATE_RetVal(int32, FGetInt32);
DECLARE_DYNAMIC_DELEGATE_RetVal(FText, FGetText);
DECLARE_DYNAMIC_DELEGATE_RetVal(FSlateColor, FGetSlateColor);
DECLARE_DYNAMIC_DELEGATE_RetVal(FLinearColor, FGetLinearColor);
DECLARE_DYNAMIC_DELEGATE_RetVal(FSlateBrush, FGetSlateBrush);
DECLARE_DYNAMIC_DELEGATE_RetVal(ESlateVisibility, FGetSlateVisibility);
DECLARE_DYNAMIC_DELEGATE_RetVal(EMouseCursor::Type, FGetMouseCursor);
DECLARE_DYNAMIC_DELEGATE_RetVal(ECheckBoxState, FGetCheckBoxState);
DECLARE_DYNAMIC_DELEGATE_RetVal(UWidget*, FGetWidget);
// Events
DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(UWidget*, FGenerateWidgetForString, FString, Item);
DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(UWidget*, FGenerateWidgetForObject, UObject*, Item);
// Events
DECLARE_DYNAMIC_DELEGATE_RetVal(FEventReply, FOnReply);
DECLARE_DYNAMIC_DELEGATE_RetVal_TwoParams(FEventReply, FOnPointerEvent, FGeometry, MyGeometry, const FPointerEvent&, MouseEvent);