【Unity】 UITKのUIElementsのイベントをR3で扱う

# 今回の目的

* UITKのUIElementのイベントをR3の`Observable`で受け取れるようにしたい。

# 定義した拡張メソッド

`Button`と`INotifyValueChanged<T>`に拡張メソッドを定義すればひとまずイベントは取れそう。

```cs
using System;
using System.Threading;
using R3;
using UnityEngine.UIElements;

namespace R3Extensions
{
    public static class R3UitkExtensions
    {
        public static Observable<Unit> OnClickAsObservable(this Button button, CancellationToken token = default)
        {
            return Observable.FromEvent(
                e => button.clicked += e,
                e => button.clicked -= e, token);
        }

        public static Observable<T> OnValueChangedAsObservable<T>(this INotifyValueChanged<T> changed,
            CancellationToken token = default)
        {
            if (changed is CallbackEventHandler handler)
            {
                return Observable.FromEvent<EventCallback<ChangeEvent<T>>, T>(
                    convert => evt => convert(evt.newValue),
                    add => handler.RegisterCallback(add),
                    remove => handler.UnregisterCallback(remove),
                    token
                );
            }

            throw new ArgumentException($"{changed} does not implement CallbackEventHandler.");
        }
    }
}
```

## 使用例

```cs
using R3;
using R3Extensions; // here
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

namespace Sandbox
{
    public class UIControlSample : MonoBehaviour
    {
        [SerializeField] private UIDocument _uiDocument;

        void Start()
        {
            // 各種UIElementを作成してUIに追加する

            var button = new Button();
            _uiDocument.rootVisualElement.Add(button);

            var dropdown = new DropdownField();
            dropdown.choices.Add("Option 1");
            dropdown.choices.Add("Option 2");
            dropdown.choices.Add("Option 3");
            _uiDocument.rootVisualElement.Add(dropdown);

            var textField = new TextField
            {
                value = "Initial Text"
            };
            _uiDocument.rootVisualElement.Add(textField);

            var toggle = new Toggle("Toggle Me");
            _uiDocument.rootVisualElement.Add(toggle);

            var slider = new Slider("Slide Me", 0, 100);
            _uiDocument.rootVisualElement.Add(slider);

            var colorField = new ColorField("Pick a Color")
            {
                value = Color.red
            };
            _uiDocument.rootVisualElement.Add(colorField);

            var layerMaskField = new LayerMaskField("Select Layer Mask")
            {
                value = LayerMask.GetMask("Default")
            };

            _uiDocument.rootVisualElement.Add(layerMaskField);

            // --- ここからイベント購読 ---

            // Button
            button.OnClickAsObservable(destroyCancellationToken)
                .Subscribe(_ => Debug.Log("Button clicked!"))
                .AddTo(this);

            // Dropdown
            dropdown.OnValueChangedAsObservable(destroyCancellationToken)
                .Subscribe(x => Debug.Log($"Dropdown changed: {x}, Index: {dropdown.index}"))
                .AddTo(this);

            // TextField
            textField.OnValueChangedAsObservable()
                .Subscribe(x => Debug.Log($"TextField changed: {x}"))
                .AddTo(this);

            // Toggle
            toggle.OnValueChangedAsObservable()
                .Subscribe(isOn => Debug.Log($"Toggle is now: {isOn}"))
                .AddTo(this);

            // Slider
            slider.OnValueChangedAsObservable()
                .Subscribe(value => Debug.Log($"Slider value changed: {value}"))
                .AddTo(this);

            // ColorField and LayerMaskField
            colorField.OnValueChangedAsObservable()
                .Subscribe(color => Debug.Log($"Color changed: {color}"))
                .AddTo(this);

            // LayerMaskField
            layerMaskField.OnValueChangedAsObservable()
                .Subscribe(mask => Debug.Log($"Layer Mask changed: {mask}"))
                .AddTo(this);

            // etc...
        }
    }
}
```

# 所感

* `UIElement`は基本的に`INotifyValueChanged<T>`を実装しており、ここからイベントを取得することができる
  * ただし`Button`の`clicked`イベントは別扱いだった
* uGUIの`InputField`にあった`onEditEnd`のイベントはUITKでは取れない？
  * Unity6.0以降では [IEditableElement](https://github.com/Unity-Technologies/UnityCsReference/blob/4b463aa72c78ec7490b7f03176bd012399881768/Modules/UIElements/Core/Controls/InputField/BaseField.cs#L29) というインタフェースが追加されているっぽくここから将来的に取れるかも
    * しかし`internal`なためアクセスはできず、ScriptReferenceにもまだ書かれていない