【UE4】Editor Utility WidgetでSelection Changed Eventを受け取る

Editor Utility Widgetで「現在選択中のアクタ」に作用するユーティリティを作りたいとき、「ところで、今、何が選択されているか」がEditor Utility Widget内で表示されていると、とても便利でわかりやすい。
ところが、調べてみたところ、たぶんBlueprintではできない匂いがする。
というわけで、C++で作ってみた。

:::note warn
UE4エディタの流儀に従っているかどうかは、あまり自信がありません…
おかしなところがあれば、教えてくださると助かります。
:::

# こういうことがしたい

エディタで選択したら、自動でEditor Utility Widgetにも反映したい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358170/c2bbf6e3-af63-2e82-ab8f-305393778039.png)

いつものテーブルの上の「Statue」を選択すると、Editor Utility Widgetでも即座に「Statue」が表示される。
Escを押すなどして選択を解除したときの表示は「None」になる。

# Blueprint側

Editor Utility WidgetのBlueprint Graphはこんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358170/5eb6c192-4728-7095-f3c0-ce509df38d6b.png)

__Event Pre Construct__

- `SinglePropertyViewCurrentSelection`は現在選択中のアクタを表示するためのもの。
    - このEditor Utility Widgetのメンバ変数を使用するので、`Set Object`には`Self`を渡す。
    - `Set Property Name`には、このEditor Utility Widgetに用意したメンバ変数の名前"CurrentSelection"を渡す。
        - CurrentSelectionはActor型の変数。
- 次の`SetEnableSelectionChangedEvent`にTrueを渡すことで、エディタのSelection Changed Event受け取り用の初期化をする。
    - 具体的には、この関数の中でSelection Changed Eventにdelegateをバインドしている。(後述のC++ソースを参照)

__Event On Editor Selection Changed__

- エディタのSelection Changed Eventを受け取って呼び出されるBlueprint Implementable Event
    - `Get Selected Level Actors`を呼び出して、現在選択中のアクタの配列を取得。
        - このノードは「Editor Scripting Utilitiesプラグイン」に含まれているので、こちらを有効にしておく必要がある。
    - 配列が空じゃなければ、最初の１個を`CurrentSelection`にセット（そのまま表示される）
    - 配列が空ならば、`CurrentSelection`をNONEにする。

#C++側

- UEditorUtililtyWidgetを親クラスとした、自作のEditorUtilityWidgetクラスを用意する。
- USelectionというクラスの`SelectionChangedEvent`にデリゲートをバインドしておけば、呼び出してくれる。

注意：

- EditorUtilityWidgetはEditorモジュール側に定義する必要がある。
- EditorUtilityWidgetを使うには、"Blutility"モジュールと "UMG"モジュールが必要。
.Build.cs に足しておくべし。
- EditorUtilityWidgetのBlueprintの新規作成時、UEditorUtilityWidget以外の親を指定できない。
気にせず作成して、後で Class Settings > 詳細パネル > Class Options > Parent Class を自作クラスに変更すれば良い。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358170/92e19d71-d640-009e-2529-64a270d2ec31.png)

## SelChangedEditorUtilityWidget.h

```C++

#pragma once

#include "CoreMinimal.h"
#include "EditorUtilityWidget.h"
#include "SelChangedEditorUtilityWidget.generated.h"


// Base class for custom editor utility widget which receives OnSelectionChanged from UE4 editor.
// Note that this class is defined in editor module.
// 
// You need to add modules to your .Build.cs:
// - "Blutility" module for UEditorUtilityWidget
// - "UMG" module for UUserWidget which is base class of UEditorUtilityWidget.
UCLASS()
class MYPROJECT_ED_API USelChangedEditorUtilityWidget : public UEditorUtilityWidget
{
	GENERATED_BODY()
	
public:

	// ctor
	USelChangedEditorUtilityWidget(const FObjectInitializer& InObjectInitializer)
		: Super(InObjectInitializer)
	{}

public:

	// Enables to receive Selection Changed Event from the editor.
	UFUNCTION(BlueprintCallable, Category = "Selection Changed Editor Utility Widget")
		void SetEnableSelectionChangedEvent(bool InEnabled);

	// Blueprint implementable event which is called when selection changed event.
	UFUNCTION(BlueprintImplementableEvent, CallInEditor, Category = "Selection Changed Editor Utility Widget")
		void OnEditorSelectionChanged();

private:

	// A delegate called when selection chaged in the editor.
	void OnSelectionChanged(UObject* InSelection);

	// a handle for bound delegate
	FDelegateHandle SelectionChangedEventHandle;
};

```

## SelChangedEditorUtilityWidget.cpp

```C++

#include "SelChangedEditorUtilityWidget.h"
#include "Engine/Selection.h" // USelection


void USelChangedEditorUtilityWidget::SetEnableSelectionChangedEvent(bool InEnabled)
{
	// bind the delegate
	if (InEnabled)
	{
		if (/*not*/!SelectionChangedEventHandle.IsValid()) // only if not bound
		{
			SelectionChangedEventHandle = USelection::SelectionChangedEvent.AddUObject(
				this, &USelChangedEditorUtilityWidget::OnSelectionChanged);
		}
	}
	// if false, unbind the delegate
	else
	{
		if (SelectionChangedEventHandle.IsValid()) // only if bound
		{
			USelection::SelectionChangedEvent.Remove(SelectionChangedEventHandle);
			SelectionChangedEventHandle.Reset();
		}
	}
}


void USelChangedEditorUtilityWidget::OnSelectionChanged(UObject* InSelection)
{
	USelection* Selection = Cast<USelection>(InSelection);
	if (Selection)
	{
		OnEditorSelectionChanged(); // call the BlueprintImplementableEvent
	}
}

```

以上です。