Skip to content

slate的构造:

要理解这些宏是什么,就要知道under the hood到底生成了什么,才能知道他们的作用。

cpp
SLATE_BEGIN_ARGS( SSubMenuButton )
		: _ShouldAppearHovered( false )
		{}
		/** The label to display on the button */
		SLATE_ATTRIBUTE( FString, Label )
		/** Called when the button is clicked */
		SLATE_EVENT( FOnClicked, OnClicked )
		/** Content to put in the button */
		SLATE_NAMED_SLOT( FArguments, FSimpleSlot, Content )
		/** Whether or not the button should appear in the hovered state */
		SLATE_ATTRIBUTE( bool, ShouldAppearHovered )
	SLATE_END_ARGS()

SLATE_BEGIN_ARGS 构造器开始

Slate定义参数,是用构造器,也就是建造者模式来构造参数。

SLATE_BEGIN_ARGS 会创建一个FArguments的内部结构体,这个结构体就是建造者模式的构造器。

cpp
#define SLATE_BEGIN_ARGS(WidgetType) \
	public:\
	struct FArguments : public TSlateBaseNamedArgs<WidgetType> \
	{ \
		typedef FArguments WidgetArgsType; \
		FORCENOINLINE FArguments()

#define SLATE_END_ARGS() \
	};

可以想到,SNew 一定返回了一个FArguments的实例,这样才能使用构造器。

过程比较复杂,简单分析一下:

cpp
//创建ListViewT
SNew(ListViewT<ItemType>)
.HandleGamepadEvents(true)
.ListItemsSource(&ListItems)

//宏展开后是这样的:
MakeTDecl<ListViewT<ItemType>>( "ListViewT<ItemType>", "ListViewBase.h", 235, RequiredArgs::MakeRequiredArgs() ) <<=  ListViewT<ItemType>::FArguments()
			.HandleGamepadEvents(true)
			.ListItemsSource(&ListItems)

这里还有个自定义的运算符重载:<<=

cpp
	/**
	 * Complete widget construction from InArgs.
	 *
	 * @param InArgs  NamedArguments from which to construct the widget.
	 *
	 * @return A reference to the widget that we constructed.
	 */
	TSharedRef<WidgetType> operator<<=( const typename WidgetType::FArguments& InArgs ) &&
	{
		_Widget->SWidgetConstruct(InArgs);
		_RequiredArgs.CallConstruct(_Widget.Get(), InArgs);
		_Widget->CacheVolatility();
		_Widget->bIsDeclarativeSyntaxConstructionCompleted = true;

		return MoveTemp(_Widget).ToSharedRef();
	}

它被设计为只能在右值上调用,这通常意味着它在临时对象上使用。

<<= 运算符在这里起到了"完成构造"的作用。它接收所有设置好的参数,并用这些参数最终构造出小部件。

通过右值引用和移动语义,减少不必要的复制。

总之,过程非常复杂,但实际使用还是很简洁高效的,知道是怎么回事就可以了。


SLATE_EVENT 构造事件宏

该macro提供了多种方法来在FArguments里构造OnSelectionChanged事件,包括:

  • 直接绑定委托
  • 静态函数绑定(Static)
  • Lambda表达式绑定(Lambda)
  • 原始指针绑定(Raw)
  • 共享指针绑定(SP)
  • UObject绑定(UObject)

解析

cpp
//using声明来创建一个类型别名
using FOnSelectionChanged       = typename TSlateDelegates< NullableItemType >::FOnSelectionChanged;
//创建一个事件类型
SLATE_EVENT( FOnSelectionChanged, OnSelectionChanged ) 

//然后,SListView的成员里定义一个,这样构造器模式里下划线那个,就可以赋值给这个真正的成员。似乎用到了通过右值引用和移动语义,减少复制。
/** Delegate to invoke when selection changes. */
FOnSelectionChanged OnSelectionChanged;

展开 SLATE_EVENT 后,会发现它提供特别多便捷设置方法,这个宏就是为我们简化添加这种构造方法的步骤。 可见,每个方法都返回static_cast<WidgetArgsType*>(this)->Me(),允许链式调用,也就形成了构造器模式。

cpp
        WidgetArgsType& OnSelectionChanged(const FOnSelectionChanged& InDelegate)
		{
			_OnSelectionChanged = InDelegate;
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		WidgetArgsType& OnSelectionChanged(FOnSelectionChanged&& InDelegate)
		{
			_OnSelectionChanged = MoveTemp(InDelegate);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <typename StaticFuncPtr, typename... VarTypes>
		WidgetArgsType& OnSelectionChanged_Static(StaticFuncPtr InFunc, VarTypes... Vars)
		{
			_OnSelectionChanged = FOnSelectionChanged::CreateStatic(InFunc, Vars...);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <typename FunctorType, typename... VarTypes>
		WidgetArgsType& OnSelectionChanged_Lambda(FunctorType&& InFunctor, VarTypes... Vars)
		{
			_OnSelectionChanged = FOnSelectionChanged::CreateLambda(Forward<FunctorType>(InFunctor), Vars...);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& OnSelectionChanged_Raw(UserClass* InUserObject, typename FOnSelectionChanged::template TMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_OnSelectionChanged = FOnSelectionChanged::CreateRaw(InUserObject, InFunc, Vars...);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& OnSelectionChanged_Raw(UserClass* InUserObject, typename FOnSelectionChanged::template TConstMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_OnSelectionChanged = FOnSelectionChanged::CreateRaw(InUserObject, InFunc, Vars...);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& OnSelectionChanged(TSharedRef<UserClass> InUserObjectRef, typename FOnSelectionChanged::template TMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_OnSelectionChanged = FOnSelectionChanged::CreateSP(InUserObjectRef, InFunc, Vars...);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& OnSelectionChanged(TSharedRef<UserClass> InUserObjectRef, typename FOnSelectionChanged::template TConstMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_OnSelectionChanged = FOnSelectionChanged::CreateSP(InUserObjectRef, InFunc, Vars...);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& OnSelectionChanged(UserClass* InUserObject, typename FOnSelectionChanged::template TMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_OnSelectionChanged = FOnSelectionChanged::CreateSP(InUserObject, InFunc, Vars...);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& OnSelectionChanged(UserClass* InUserObject, typename FOnSelectionChanged::template TConstMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_OnSelectionChanged = FOnSelectionChanged::CreateSP(InUserObject, InFunc, Vars...);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& OnSelectionChanged_UObject(UserClass* InUserObject, typename FOnSelectionChanged::template TMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_OnSelectionChanged = FOnSelectionChanged::CreateUObject(InUserObject, InFunc, Vars...);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& OnSelectionChanged_UObject(UserClass* InUserObject, typename FOnSelectionChanged::template TConstMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_OnSelectionChanged = FOnSelectionChanged::CreateUObject(InUserObject, InFunc, Vars...);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		FOnSelectionChanged _OnSelectionChanged;

用法

引擎内部的帮助函数,提供构造帮助类:ListView construction helpers

可以看到,用到了OnSelectionChanged_UObject函数设置值,因为Implementer是Uobject。

cpp
template <template<typename> class ListViewT = SListView, typename UListViewBaseT>
	static TSharedRef<ListViewT<ItemType>> ConstructListView(UListViewBaseT* Implementer, 
		const TArray<ItemType>& ListItems,
		const FListViewConstructArgs& Args = FListViewConstructArgs())
	{
		static_assert(TIsDerivedFrom<ListViewT<ItemType>, SListView<ItemType>>::IsDerived, "ConstructListView can only construct instances of SListView classes");
		return SNew(ListViewT<ItemType>)
			.HandleGamepadEvents(true)
			.ListItemsSource(&ListItems)
			.IsFocusable(Args.bAllowFocus)
			.ClearSelectionOnClick(Args.bClearSelectionOnClick)
			.ConsumeMouseWheel(Args.ConsumeMouseWheel)
			.SelectionMode(Args.SelectionMode)
			.ReturnFocusToSelection(Args.bReturnFocusToSelection)
			.Orientation(Args.Orientation)
			.ListViewStyle(Args.ListViewStyle)
			.ScrollBarStyle(Args.ScrollBarStyle)
			.PreventThrottling(Args.bPreventThrottling)
			.OnGenerateRow_UObject(Implementer, &UListViewBaseT::HandleGenerateRow)
			.OnSelectionChanged_UObject(Implementer, &UListViewBaseT::HandleSelectionChanged)

SLATE_ATTRIBUTE 属性构造宏

该宏用来在FArguments里构造属性,支持以下几种方式,包括:

  • 直接设置值
  • 静态函数(Static)
  • Lambda表达式(Lambda)
  • 原始指针(Raw)
  • 共享指针(SP)
  • UObject方法(UObject)

解析

cpp
SLATE_ATTRIBUTE( float, ItemHeight )

展开后, 新增的属性以下滑线开头。

cpp
		TAttribute<float> _ItemHeight;

		WidgetArgsType& ItemHeight(TAttribute<float> InAttribute)
		{
			_ItemHeight = MoveTemp(InAttribute);
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <typename... VarTypes>
		WidgetArgsType&
		ItemHeight_Static(TIdentity_T<typename TAttribute<float>::FGetter::template TFuncPtr<VarTypes...>> InFunc, VarTypes... Vars)
		{
			_ItemHeight = TAttribute<float>::Create(TAttribute<float>::FGetter::CreateStatic(InFunc, Vars...));
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		WidgetArgsType& ItemHeight_Lambda(TFunction<float(void)>&& InFunctor)
		{
			_ItemHeight = TAttribute<float>::Create(Forward<TFunction<float(void)>>(InFunctor));
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& ItemHeight_Raw(UserClass* InUserObject, typename TAttribute<float>::FGetter::template TConstMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_ItemHeight = TAttribute<float>::Create(TAttribute<float>::FGetter::CreateRaw(InUserObject, InFunc, Vars...));
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& ItemHeight(TSharedRef<UserClass> InUserObjectRef, typename TAttribute<float>::FGetter::template TConstMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_ItemHeight = TAttribute<float>::Create(TAttribute<float>::FGetter::CreateSP(InUserObjectRef, InFunc, Vars...));
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& ItemHeight(UserClass* InUserObject, typename TAttribute<float>::FGetter::template TConstMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_ItemHeight = TAttribute<float>::Create(TAttribute<float>::FGetter::CreateSP(InUserObject, InFunc, Vars...));
			return static_cast<WidgetArgsType*>(this)->Me();
		}

		template <class UserClass, typename... VarTypes>
		WidgetArgsType& ItemHeight_UObject(UserClass* InUserObject, typename TAttribute<float>::FGetter::template TConstMethodPtr<UserClass, VarTypes...> InFunc, VarTypes... Vars)
		{
			_ItemHeight = TAttribute<float>::Create(TAttribute<float>::FGetter::CreateUObject(InUserObject, InFunc, Vars...));
			return static_cast<WidgetArgsType*>(this)->Me();
		}