#はじめに
最近作っているプラグインでFSlateApplicationから取得したウィンドウからBlueprintやMaterialのグラフエディタのインスタンスを取得するために、Widgetの子を再帰的に辿って取得する処理が必要になり、そのためにSlateのクラス(SWidget
を継承したクラス)をキャストする必要がありました。
※SWidget
はUObject
を継承していないため、標準のCast関数テンプレートは使えません。
#キャストする関数(マクロ?)
GraphPrinterCore.h
namespace CastSlateWidgetPrivate
{
template<class To, class From>
TSharedPtr<To> CastSlateWidget(TSharedPtr<From> FromPtr, const FName& ToClassName)
{
static_assert(TIsDerivedFrom<From, SWidget>::IsDerived, "This implementation wasn't tested for a filter that isn't a child of SWidget.");
static_assert(TIsDerivedFrom<To, SWidget>::IsDerived, "This implementation wasn't tested for a filter that isn't a child of SWidget.");
if (FromPtr.IsValid())
{
if (FromPtr->GetType() == ToClassName)
{
return StaticCastSharedPtr<To>(FromPtr);
}
}
return nullptr;
}
}
#define CAST_SLATE_WIDGET(ToClass, FromPtr) CastSlateWidgetPrivate::CastSlateWidget<ToClass>(FromPtr, #ToClass)
SWidget
はUObject
ではないですが、自身のクラスの名前を保持しているためキャスト元のSWidget::GetType
から取得した名前とキャスト先のクラス名を文字列化させたものを比較して、同一ならStaticCastSharedPtr
でキャストします。
StaticCastSharedPtr
はUObject以外(例えば構造体など)もTSharedPtr
でラップすればキャストできる関数です。しかし、名前がDynamicCastSharedPtr
でない所から分かる通り、もしキャストに失敗してもnullptrを返してくれません。そのため、クラス名の文字列での判定をしています。
#define SNew( WidgetType, ... ) \
MakeTDecl<WidgetType>( #WidgetType, __FILE__, __LINE__, RequiredArgs::MakeRequiredArgs(__VA_ARGS__) ) <<= TYPENAME_OUTSIDE_TEMPLATE WidgetType::FArguments()
#define SAssignNew( ExposeAs, WidgetType, ... ) \
MakeTDecl<WidgetType>( #WidgetType, __FILE__, __LINE__, RequiredArgs::MakeRequiredArgs(__VA_ARGS__) ) . Expose( ExposeAs ) <<= TYPENAME_OUTSIDE_TEMPLATE WidgetType::FArguments()
余談ですが、軽く調べた感じだとSWidget::GetType
から取得できる文字列はSlateを書く際に使うSNew
のマクロ内で引数のクラス名を文字列化して変数に入れているようでした。
そのため、UObject
系列のクラス名が接頭辞を抜いた形(例えばUObject
なら「Object」)になっているのに対して、SWidget::GetType
から取得できる名前は接頭辞付きなため、上記コードのようにマクロでキャスト先のクラス名をそのまま文字列化させています。
TSharedPtr<SGraphEditor> FGraphPrinterCore::FindGraphEditor(TSharedPtr<SWidget> SearchTarget)
{
TArray<TSharedPtr<SWidget>> ChildWidgets;
CollectAllChildWidgets(SearchTarget, ChildWidgets);
for (const auto& ChildWidget : ChildWidgets)
{
TSharedPtr<SGraphEditor> GraphEditor = CAST_SLATE_WIDGET(SGraphEditor, ChildWidget);
if (GraphEditor.IsValid())
{
return GraphEditor;
}
}
return nullptr;
}
使用する際はこのようになります。
こちらははじめにでお話した子ウィジェットからグラフエディタを取得する処理です。
#おわりに
今回はウィンドウから子ウィジェットを走査する形は単純に重そうなので他の処理にするのも良いですが、この方法だとこちらで紹介されているWidget Reflector
で確認できるWidgetに必ず辿り着ける点が魅力かと思われます。取得方法は後で考えるからとにかくこのWidgetに対して何かしたいなどの試作作業でも役に立つかもしれません。
そもそもSlateのクラスをキャストしようとすること自体あまりないかと思いますが、もしやろうとしている方の参考になれば幸いです。
明日は、@shiratori1221さんの「GameplayAbilityとアニメーションモンタージュを使ったコンボについて」です!