🍪

【UE5】SlateをEditorUtilityWidgetで使う際にハマったこと

2024/12/24に公開

概要

この記事は一人アドベントカレンダー by ダリアの24日目の記事です。

今回はSlateだけで使える ClassFilterという機能をEditorUtilityWidgetで呼び出すように実装した際に得た知見を備忘録も兼ねてまとめようと思います。

想定してるイメージは下記となります。
https://x.com/daria_nicht/status/1870125705629184047

Slateの参考になれば幸いです。

環境

UE5.4.4

やったこと

まずクラスの全体図を書いて詰まったところを説明します。

ExampleClassFilter.h
#pragma once

#include "CoreMinimal.h"
#include "Components/ContentWidget.h"
#include "Filters/SFilterBar.h"
#include "ClassFilterWidget.generated.h"

struct ExampleType : TSharedFromThis<ExampleType>
{
public:
    virtual ~ExampleType() {}
protected:
    ExampleType() {}

};

class SClassFilterWidget : public SCompoundWidget
{
public:
    SLATE_BEGIN_ARGS(SClassFilterWidget)
    {
        
    }
    SLATE_END_ARGS()
    void Construct(const FArguments& InArgs);
    bool CompareItemWithClassName(ExampleType InItem, const TSet<FTopLevelAssetPath>& AssetClassPaths);
    void OnFilterChanged();
    TSharedPtr<SFilterBar<ExampleType>> FilterListPtr;
    TSharedPtr<SSearchBox> SearchBoxPtr;
};

UCLASS()
class EXAMPLEEUWCLASSFILTEREDITOR_API UClassFilterWidget : public UContentWidget
{
    GENERATED_BODY()
protected:
    virtual TSharedRef<SWidget> RebuildWidget() override;
    virtual void ReleaseSlateResources(bool bReleaseChildren) override;
protected:
    TSharedPtr<SClassFilterWidget> ClassFilterWidgetPtr;
};
#include "ClassFilterWidget.h"

#include "Widgets/Input/SSearchBox.h"

void SClassFilterWidget::Construct(const FArguments& InArgs)
{
    FilterListPtr = SNew(SFilterBar<ExampleType>)
        .OnCompareItemWithClassNames(this,&SClassFilterWidget::CompareItemWithClassName)
        .OnFilterChanged(this, &SClassFilterWidget::OnFilterChanged);;
    ChildSlot
    [
        FilterListPtr.ToSharedRef()
    ];
}

TSharedRef<SWidget> UClassFilterWidget::RebuildWidget()
{
    ClassFilterWidgetPtr = SNew(SClassFilterWidget);

    return SNew(SVerticalBox)
        +SVerticalBox::Slot()
        [
            ClassFilterWidgetPtr.ToSharedRef()
        ];
}

void UClassFilterWidget::ReleaseSlateResources(bool bReleaseChildren)
{
    Super::ReleaseSlateResources(bReleaseChildren);
    ClassFilterWidgetPtr.Reset();
}

void SClassFilterWidget::OnFilterChanged()
{
    UE_LOG(LogTemp,Log,TEXT("ClassFilterWidget::OnFilterChanged"));
}
bool SClassFilterWidget::CompareItemWithClassName(ExampleType InItem,
	const TSet<FTopLevelAssetPath>& AssetClassPaths)
{
    return true;
}

SharedPtrで保持するはずがSharedRefになっていた

最初はこう書いてました。

//本当はTSharedPtrにしないといけない
TSharedRef<SClassFilterWidget> VerticalBoxPtr;

下記でビルドすると

TSharedRef<SWidget> UClassFilterWidget::RebuildWidget()
{
    ClassFilterWidgetPtr = SNew(SClassFilterWidget)
    return ClassFilterWidgetPtr.ToSharedRef()
}

以下のコンパイルエラーが出てました。

Trying to use new with any child from SWidget produces:
error C2248: ‘FSlateControlledConstruction::operator new: cannot access private member declared in class ‘FSlateControlledConstruction’

解決策はSharedRefをSharedPtrに変えることでした。

追加したSlateが表示されない

この部分が最初実装した際に何も表示されてない状態になっていました。

FilterListPtr = SNew(SFilterBar<ExampleType>)
    .OnCompareItemWithClassNames(this,&SClassFilterWidget::CompareItemWithClassName)
    .OnFilterChanged(this, &SClassFilterWidget::OnFilterChanged);

ウィジェットリフレクタで作成は出来たのがわかったので、試しにSlotで候補を探していた所ChildSlotというのを見つけて、それを追加したら表示されるようになりました。

FilterListPtr = SNew(SFilterBar<ExampleType>)
    .OnCompareItemWithClassNames(this,&SClassFilterWidget::CompareItemWithClassName)
    .OnFilterChanged(this, &SClassFilterWidget::OnFilterChanged);

//下記を呼ばないと表示されない
+ChildSlot
+[
+    FilterListPtr.ToSharedRef()
+];

記事は以上となります。
ここまで読んでいただきありがとうございます。

参考

【UE5】詳細で良く見るアセットの種類にクラスでフィルターをかけてコンボボックスから候補一覧を選択するとそのサムネイルも表示してくれるウィジェットを EditorUtilityWidget で使う方法【C++】

Unreal スマート ポインタ ライブラリ

C++ Slate Compound Widget Not showing up in editor window

Discussion