UE4 slateUI slot
void Construct( const FArguments& Args ) { SetCanTick(false); this->ChildSlot [ SNew(SVerticalBox) + SVerticalBox::Slot() .Padding( FMargin( 0.0f, 0.0f, 2.0f, 0.0f ) ) [ FSlateApplicationBase::Get().MakeImage( FSlateApplicationBase::Get().GetAppIcon(), Args._IconColorAndOpacity, EVisibility::HitTestInvisible ) ] ]; }
slate代码这样写的,一开始学感觉很奇怪
其实这是为了减少代码而设计的.
首先这个widget继承自SCompoundWidget,这个可以有子控件,SCompoundWidget继承自SWidget,这个SWidget是基础的控件,它不可以有子控件
SCompoundWidget里面有成员 ChildSlot ,是这样定义的 FSimpleSlot ChildSlot; 这个是子控件容器
/** A slot that support alignment of content and padding */ class SLATECORE_API FSimpleSlot : public TSupportsOneChildMixin<FSimpleSlot>, public TSupportsContentAlignmentMixin<FSimpleSlot>, public TSupportsContentPaddingMixin<FSimpleSlot> { public: FSimpleSlot(SWidget* InParent) : TSupportsOneChildMixin<FSimpleSlot>(InParent) , TSupportsContentAlignmentMixin<FSimpleSlot>(HAlign_Fill, VAlign_Fill) { } };
下面是TSupportsOneChildMixin定义
/** * Widgets that will only have one child can return an instance of FOneChild. */ template <typename MixedIntoType> class TSupportsOneChildMixin : public FChildren, public TSlotBase<MixedIntoType> { public: TSupportsOneChildMixin(SWidget* InOwner) : FChildren(InOwner) , TSlotBase<MixedIntoType>() { this->RawParentPtr = InOwner; } virtual int32 Num() const override { return 1; } virtual TSharedRef<SWidget> GetChildAt( int32 ChildIndex ) override { check(ChildIndex == 0); return FSlotBase::GetWidget(); } virtual TSharedRef<const SWidget> GetChildAt( int32 ChildIndex ) const override { check(ChildIndex == 0); return FSlotBase::GetWidget(); } private: virtual const FSlotBase& GetSlotAt(int32 ChildIndex) const override { check(ChildIndex == 0); return *this; } };
而TSlotBase有重写[] 方法
template<typename SlotType> class TSlotBase : public FSlotBase { public: TSlotBase() : FSlotBase() {} TSlotBase( const TSharedRef<SWidget>& InWidget ) : FSlotBase( InWidget ) {} SlotType& operator[]( const TSharedRef<SWidget>& InChildWidget ) { this->AttachWidget(InChildWidget); return (SlotType&)(*this); } SlotType& Expose( SlotType*& OutVarToInit ) { OutVarToInit = (SlotType*)this; return (SlotType&)(*this); } };
所以有上面那个方括号里面写代码的内容,意思就是方括号里面的widget 加到子控件数组里
template <typename MixedIntoType> class TSupportsContentAlignmentMixin { public: TSupportsContentAlignmentMixin(const EHorizontalAlignment InHAlign, const EVerticalAlignment InVAlign) : HAlignment( InHAlign ) , VAlignment( InVAlign ) { } MixedIntoType& HAlign( EHorizontalAlignment InHAlignment ) { HAlignment = InHAlignment; return *(static_cast<MixedIntoType*>(this)); } MixedIntoType& VAlign( EVerticalAlignment InVAlignment ) { VAlignment = InVAlignment; return *(static_cast<MixedIntoType*>(this)); } EHorizontalAlignment HAlignment; EVerticalAlignment VAlignment; }; template <typename MixedIntoType> class TSupportsContentPaddingMixin { public: MixedIntoType& Padding( const TAttribute<FMargin> InPadding ) { SlotPadding = InPadding; return *(static_cast<MixedIntoType*>(this)); } MixedIntoType& Padding( float Uniform ) { SlotPadding = FMargin(Uniform); return *(static_cast<MixedIntoType*>(this)); } MixedIntoType& Padding( float Horizontal, float Vertical ) { SlotPadding = FMargin(Horizontal, Vertical); return *(static_cast<MixedIntoType*>(this)); } MixedIntoType& Padding( float Left, float Top, float Right, float Bottom ) { SlotPadding = FMargin(Left, Top, Right, Bottom); return *(static_cast<MixedIntoType*>(this)); } TAttribute< FMargin > SlotPadding; };
而slot继承自TSupportsContentAlignmentMixin和TSupportsContentPaddingMixin, 所以具备 .HAlign .VAlign 和 .Padding功能
DeclarativeSyntaxSupport.h 这个文件里面包含了一些宏定义
/** * Use this macro between SLATE_BEGIN_ARGS and SLATE_END_ARGS * in order to add support for slots. */ #define SLATE_SUPPORTS_SLOT( SlotType ) \ TArray< SlotType* > Slots; \ WidgetArgsType& operator + (SlotType& SlotToAdd) \ { \ Slots.Add( &SlotToAdd ); \ return *this; \ }
而widget里面调用了这个
SLATE_BEGIN_ARGS( SHorizontalBox ) { _Visibility = EVisibility::SelfHitTestInvisible; } SLATE_SUPPORTS_SLOT(SHorizontalBox::FSlot) SLATE_END_ARGS()
如果Widget只有一个Slot,可以直接用[ ]来添加widget
#define SLATE_DEFAULT_SLOT( DeclarationType, SlotName ) \ SLATE_NAMED_SLOT(DeclarationType, SlotName) ; \ DeclarationType & operator[]( const TSharedRef<SWidget> InChild ) \ { \ _##SlotName.Widget = InChild; \ return *this; \ }
这里定义了一个slots数组, 添加了 + 的重载符,就是可以向这个数组里面加 slot,
而类里面有一个这个静态函数,调用的时候这样SVerticalBox::Slot() 表示new 一个slot
static FSlot& Slot() { return *(new FSlot()); }
然后你就可以这样写了 + SVerticalBox::Slot()
抄个简单的,后面自己的ui也改成这个方式
#include<stdio.h> #include<vector> enum EHorizontalAlignment { HAlign_Fill , HAlign_Left, HAlign_Center, HAlign_Right, }; enum EVerticalAlignment { VAlign_Fill, VAlign_Top, VAlign_Center, VAlign_Bottom, }; struct FMargin { float Left; float Top; float Right; float Bottom; FMargin() : Left(0.0f) , Top(0.0f) , Right(0.0f) , Bottom(0.0f) { } FMargin(float UniformMargin) : Left(UniformMargin) , Top(UniformMargin) , Right(UniformMargin) , Bottom(UniformMargin) { } FMargin(float Horizontal, float Vertical) : Left(Horizontal) , Top(Vertical) , Right(Horizontal) , Bottom(Vertical) { } FMargin(float InLeft, float InTop, float InRight, float InBottom) : Left(InLeft) , Top(InTop) , Right(InRight) , Bottom(InBottom) { } }; template<typename MixedIntoType> class TSupportsContentAlignmentMixin { public: TSupportsContentAlignmentMixin(const EHorizontalAlignment InHAlign, const EVerticalAlignment InVAlign) : HAlignment(InHAlign) , VAlignment(InVAlign) { } MixedIntoType& HAlign(EHorizontalAlignment InHAlignment) { HAlignment = InHAlignment; return *(static_cast<MixedIntoType*>(this)); } MixedIntoType& VAlign(EVerticalAlignment InVAlignment) { VAlignment = InVAlignment; return *(static_cast<MixedIntoType*>(this)); } EHorizontalAlignment HAlignment; EVerticalAlignment VAlignment; }; template <typename MixedIntoType> class TSupportsContentPaddingMixin { public: MixedIntoType& Padding(const FMargin InPadding) { SlotPadding = InPadding; return *(static_cast<MixedIntoType*>(this)); } MixedIntoType& Padding(float Uniform) { SlotPadding = FMargin(Uniform); return *(static_cast<MixedIntoType*>(this)); } MixedIntoType& Padding(float Horizontal, float Vertical) { SlotPadding = FMargin(Horizontal, Vertical); return *(static_cast<MixedIntoType*>(this)); } MixedIntoType& Padding(float Left, float Top, float Right, float Bottom) { SlotPadding = FMargin(Left, Top, Right, Bottom); return *(static_cast<MixedIntoType*>(this)); } FMargin SlotPadding; }; class Widget; class FSlotBase { public: FSlotBase(){} FSlotBase(Widget& InWidget) { m_Widget = &InWidget; } void AttachWidget(Widget& InWidget) { m_Widget = &InWidget; } const Widget* GetWidget() const { return m_Widget; } private: // non-copyable FSlotBase& operator=(const FSlotBase&); FSlotBase(const FSlotBase&); Widget* m_Widget; }; template<typename SlotType> class TSlotBase : public FSlotBase { public: TSlotBase() : FSlotBase() {} TSlotBase(Widget* InWidget) : FSlotBase(InWidget) {} SlotType& operator[](Widget& InChildWidget) { this->AttachWidget(InChildWidget); return (SlotType&)(*this); } SlotType& Expose(SlotType*& OutVarToInit) { OutVarToInit = (SlotType*)this; return (SlotType&)(*this); } }; class Widget { public: class FSlot :public TSlotBase<FSlot>,public TSupportsContentAlignmentMixin<FSlot>,public TSupportsContentPaddingMixin<FSlot> { public: FSlot() :TSupportsContentAlignmentMixin<FSlot>(HAlign_Fill, VAlign_Fill) {} }; Widget& operator + (FSlot& SlotToAdd) { Slots.push_back(&SlotToAdd); return *this; } static FSlot& Slot() { return *(new FSlot()); } FSlot& AddSlot() { FSlot* NewSlot = new FSlot(); Slots.push_back(NewSlot); return *NewSlot; } public: std::vector<FSlot*> Slots; }; template<typename WidgetType> struct TDecl { void Make() { m_Widget = new WidgetType(); } WidgetType* m_Widget; }; Widget SNew() { Widget* temp = new Widget(); return *temp; } int main() { Widget twidget ; twidget.AddSlot() [ SNew() + Widget::Slot() .Padding(1) .HAlign(HAlign_Center) .VAlign(VAlign_Bottom) [ SNew() ] + Widget::Slot() .Padding(1) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew() ] ]; twidget+ Widget::Slot() [ SNew() ]; return 0; }