#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT using System; using System.Threading; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; namespace Cysharp.Threading.Tasks { public static partial class UnityAsyncExtensions { public static AsyncUnityEventHandler GetAsyncEventHandler(this UnityEvent unityEvent, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(unityEvent, cancellationToken, false); } public static UniTask OnInvokeAsync(this UnityEvent unityEvent, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(unityEvent, cancellationToken, true).OnInvokeAsync(); } public static IUniTaskAsyncEnumerable OnInvokeAsAsyncEnumerable(this UnityEvent unityEvent, CancellationToken cancellationToken) { return new UnityEventHandlerAsyncEnumerable(unityEvent, cancellationToken); } public static AsyncUnityEventHandler GetAsyncEventHandler(this UnityEvent unityEvent, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(unityEvent, cancellationToken, false); } public static UniTask OnInvokeAsync(this UnityEvent unityEvent, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(unityEvent, cancellationToken, true).OnInvokeAsync(); } public static IUniTaskAsyncEnumerable OnInvokeAsAsyncEnumerable(this UnityEvent unityEvent, CancellationToken cancellationToken) { return new UnityEventHandlerAsyncEnumerable(unityEvent, cancellationToken); } public static IAsyncClickEventHandler GetAsyncClickEventHandler(this Button button) { return new AsyncUnityEventHandler(button.onClick, button.GetCancellationTokenOnDestroy(), false); } public static IAsyncClickEventHandler GetAsyncClickEventHandler(this Button button, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(button.onClick, cancellationToken, false); } public static UniTask OnClickAsync(this Button button) { return new AsyncUnityEventHandler(button.onClick, button.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); } public static UniTask OnClickAsync(this Button button, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(button.onClick, cancellationToken, true).OnInvokeAsync(); } public static IUniTaskAsyncEnumerable OnClickAsAsyncEnumerable(this Button button) { return new UnityEventHandlerAsyncEnumerable(button.onClick, button.GetCancellationTokenOnDestroy()); } public static IUniTaskAsyncEnumerable OnClickAsAsyncEnumerable(this Button button, CancellationToken cancellationToken) { return new UnityEventHandlerAsyncEnumerable(button.onClick, cancellationToken); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this Toggle toggle) { return new AsyncUnityEventHandler(toggle.onValueChanged, toggle.GetCancellationTokenOnDestroy(), false); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this Toggle toggle, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(toggle.onValueChanged, cancellationToken, false); } public static UniTask OnValueChangedAsync(this Toggle toggle) { return new AsyncUnityEventHandler(toggle.onValueChanged, toggle.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); } public static UniTask OnValueChangedAsync(this Toggle toggle, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(toggle.onValueChanged, cancellationToken, true).OnInvokeAsync(); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this Toggle toggle) { return new UnityEventHandlerAsyncEnumerable(toggle.onValueChanged, toggle.GetCancellationTokenOnDestroy()); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this Toggle toggle, CancellationToken cancellationToken) { return new UnityEventHandlerAsyncEnumerable(toggle.onValueChanged, cancellationToken); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this Scrollbar scrollbar) { return new AsyncUnityEventHandler(scrollbar.onValueChanged, scrollbar.GetCancellationTokenOnDestroy(), false); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this Scrollbar scrollbar, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(scrollbar.onValueChanged, cancellationToken, false); } public static UniTask OnValueChangedAsync(this Scrollbar scrollbar) { return new AsyncUnityEventHandler(scrollbar.onValueChanged, scrollbar.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); } public static UniTask OnValueChangedAsync(this Scrollbar scrollbar, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(scrollbar.onValueChanged, cancellationToken, true).OnInvokeAsync(); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this Scrollbar scrollbar) { return new UnityEventHandlerAsyncEnumerable(scrollbar.onValueChanged, scrollbar.GetCancellationTokenOnDestroy()); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this Scrollbar scrollbar, CancellationToken cancellationToken) { return new UnityEventHandlerAsyncEnumerable(scrollbar.onValueChanged, cancellationToken); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this ScrollRect scrollRect) { return new AsyncUnityEventHandler(scrollRect.onValueChanged, scrollRect.GetCancellationTokenOnDestroy(), false); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this ScrollRect scrollRect, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(scrollRect.onValueChanged, cancellationToken, false); } public static UniTask OnValueChangedAsync(this ScrollRect scrollRect) { return new AsyncUnityEventHandler(scrollRect.onValueChanged, scrollRect.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); } public static UniTask OnValueChangedAsync(this ScrollRect scrollRect, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(scrollRect.onValueChanged, cancellationToken, true).OnInvokeAsync(); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this ScrollRect scrollRect) { return new UnityEventHandlerAsyncEnumerable(scrollRect.onValueChanged, scrollRect.GetCancellationTokenOnDestroy()); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this ScrollRect scrollRect, CancellationToken cancellationToken) { return new UnityEventHandlerAsyncEnumerable(scrollRect.onValueChanged, cancellationToken); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this Slider slider) { return new AsyncUnityEventHandler(slider.onValueChanged, slider.GetCancellationTokenOnDestroy(), false); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this Slider slider, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(slider.onValueChanged, cancellationToken, false); } public static UniTask OnValueChangedAsync(this Slider slider) { return new AsyncUnityEventHandler(slider.onValueChanged, slider.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); } public static UniTask OnValueChangedAsync(this Slider slider, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(slider.onValueChanged, cancellationToken, true).OnInvokeAsync(); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this Slider slider) { return new UnityEventHandlerAsyncEnumerable(slider.onValueChanged, slider.GetCancellationTokenOnDestroy()); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this Slider slider, CancellationToken cancellationToken) { return new UnityEventHandlerAsyncEnumerable(slider.onValueChanged, cancellationToken); } public static IAsyncEndEditEventHandler GetAsyncEndEditEventHandler(this InputField inputField) { return new AsyncUnityEventHandler(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), false); } public static IAsyncEndEditEventHandler GetAsyncEndEditEventHandler(this InputField inputField, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(inputField.onEndEdit, cancellationToken, false); } public static UniTask OnEndEditAsync(this InputField inputField) { return new AsyncUnityEventHandler(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); } public static UniTask OnEndEditAsync(this InputField inputField, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(inputField.onEndEdit, cancellationToken, true).OnInvokeAsync(); } public static IUniTaskAsyncEnumerable OnEndEditAsAsyncEnumerable(this InputField inputField) { return new UnityEventHandlerAsyncEnumerable(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy()); } public static IUniTaskAsyncEnumerable OnEndEditAsAsyncEnumerable(this InputField inputField, CancellationToken cancellationToken) { return new UnityEventHandlerAsyncEnumerable(inputField.onEndEdit, cancellationToken); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this InputField inputField) { return new AsyncUnityEventHandler(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), false); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this InputField inputField, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(inputField.onValueChanged, cancellationToken, false); } public static UniTask OnValueChangedAsync(this InputField inputField) { return new AsyncUnityEventHandler(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); } public static UniTask OnValueChangedAsync(this InputField inputField, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(inputField.onValueChanged, cancellationToken, true).OnInvokeAsync(); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this InputField inputField) { return new UnityEventHandlerAsyncEnumerable(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy()); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this InputField inputField, CancellationToken cancellationToken) { return new UnityEventHandlerAsyncEnumerable(inputField.onValueChanged, cancellationToken); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this Dropdown dropdown) { return new AsyncUnityEventHandler(dropdown.onValueChanged, dropdown.GetCancellationTokenOnDestroy(), false); } public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this Dropdown dropdown, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(dropdown.onValueChanged, cancellationToken, false); } public static UniTask OnValueChangedAsync(this Dropdown dropdown) { return new AsyncUnityEventHandler(dropdown.onValueChanged, dropdown.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); } public static UniTask OnValueChangedAsync(this Dropdown dropdown, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(dropdown.onValueChanged, cancellationToken, true).OnInvokeAsync(); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this Dropdown dropdown) { return new UnityEventHandlerAsyncEnumerable(dropdown.onValueChanged, dropdown.GetCancellationTokenOnDestroy()); } public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this Dropdown dropdown, CancellationToken cancellationToken) { return new UnityEventHandlerAsyncEnumerable(dropdown.onValueChanged, cancellationToken); } } public interface IAsyncClickEventHandler : IDisposable { UniTask OnClickAsync(); } public interface IAsyncValueChangedEventHandler : IDisposable { UniTask OnValueChangedAsync(); } public interface IAsyncEndEditEventHandler : IDisposable { UniTask OnEndEditAsync(); } // for TMP_PRO public interface IAsyncEndTextSelectionEventHandler : IDisposable { UniTask OnEndTextSelectionAsync(); } public interface IAsyncTextSelectionEventHandler : IDisposable { UniTask OnTextSelectionAsync(); } public interface IAsyncDeselectEventHandler : IDisposable { UniTask OnDeselectAsync(); } public interface IAsyncSelectEventHandler : IDisposable { UniTask OnSelectAsync(); } public interface IAsyncSubmitEventHandler : IDisposable { UniTask OnSubmitAsync(); } internal class TextSelectionEventConverter : UnityEvent<(string, int, int)>, IDisposable { readonly UnityEvent innerEvent; readonly UnityAction invokeDelegate; public TextSelectionEventConverter(UnityEvent unityEvent) { this.innerEvent = unityEvent; this.invokeDelegate = InvokeCore; innerEvent.AddListener(invokeDelegate); } void InvokeCore(string item1, int item2, int item3) { innerEvent.Invoke(item1, item2, item3); } public void Dispose() { innerEvent.RemoveListener(invokeDelegate); } } public class AsyncUnityEventHandler : IUniTaskSource, IDisposable, IAsyncClickEventHandler { static Action cancellationCallback = CancellationCallback; readonly UnityAction action; readonly UnityEvent unityEvent; CancellationToken cancellationToken; CancellationTokenRegistration registration; bool isDisposed; bool callOnce; UniTaskCompletionSourceCore core; public AsyncUnityEventHandler(UnityEvent unityEvent, CancellationToken cancellationToken, bool callOnce) { this.cancellationToken = cancellationToken; if (cancellationToken.IsCancellationRequested) { isDisposed = true; return; } this.action = Invoke; this.unityEvent = unityEvent; this.callOnce = callOnce; unityEvent.AddListener(action); if (cancellationToken.CanBeCanceled) { registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); } TaskTracker.TrackActiveTask(this, 3); } public UniTask OnInvokeAsync() { core.Reset(); if (isDisposed) { core.TrySetCanceled(this.cancellationToken); } return new UniTask(this, core.Version); } void Invoke() { core.TrySetResult(AsyncUnit.Default); } static void CancellationCallback(object state) { var self = (AsyncUnityEventHandler)state; self.Dispose(); } public void Dispose() { if (!isDisposed) { isDisposed = true; TaskTracker.RemoveTracking(this); registration.Dispose(); if (unityEvent != null) { unityEvent.RemoveListener(action); } core.TrySetCanceled(cancellationToken); } } UniTask IAsyncClickEventHandler.OnClickAsync() { return OnInvokeAsync(); } void IUniTaskSource.GetResult(short token) { try { core.GetResult(token); } finally { if (callOnce) { Dispose(); } } } UniTaskStatus IUniTaskSource.GetStatus(short token) { return core.GetStatus(token); } UniTaskStatus IUniTaskSource.UnsafeGetStatus() { return core.UnsafeGetStatus(); } void IUniTaskSource.OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } } public class AsyncUnityEventHandler : IUniTaskSource, IDisposable, IAsyncValueChangedEventHandler, IAsyncEndEditEventHandler , IAsyncEndTextSelectionEventHandler, IAsyncTextSelectionEventHandler, IAsyncDeselectEventHandler, IAsyncSelectEventHandler, IAsyncSubmitEventHandler { static Action cancellationCallback = CancellationCallback; readonly UnityAction action; readonly UnityEvent unityEvent; CancellationToken cancellationToken; CancellationTokenRegistration registration; bool isDisposed; bool callOnce; UniTaskCompletionSourceCore core; public AsyncUnityEventHandler(UnityEvent unityEvent, CancellationToken cancellationToken, bool callOnce) { this.cancellationToken = cancellationToken; if (cancellationToken.IsCancellationRequested) { isDisposed = true; return; } this.action = Invoke; this.unityEvent = unityEvent; this.callOnce = callOnce; unityEvent.AddListener(action); if (cancellationToken.CanBeCanceled) { registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); } TaskTracker.TrackActiveTask(this, 3); } public UniTask OnInvokeAsync() { core.Reset(); if (isDisposed) { core.TrySetCanceled(this.cancellationToken); } return new UniTask(this, core.Version); } void Invoke(T result) { core.TrySetResult(result); } static void CancellationCallback(object state) { var self = (AsyncUnityEventHandler)state; self.Dispose(); } public void Dispose() { if (!isDisposed) { isDisposed = true; TaskTracker.RemoveTracking(this); registration.Dispose(); if (unityEvent != null) { // Dispose inner delegate for TextSelectionEventConverter if (unityEvent is IDisposable disp) { disp.Dispose(); } unityEvent.RemoveListener(action); } core.TrySetCanceled(); } } UniTask IAsyncValueChangedEventHandler.OnValueChangedAsync() { return OnInvokeAsync(); } UniTask IAsyncEndEditEventHandler.OnEndEditAsync() { return OnInvokeAsync(); } UniTask IAsyncEndTextSelectionEventHandler.OnEndTextSelectionAsync() { return OnInvokeAsync(); } UniTask IAsyncTextSelectionEventHandler.OnTextSelectionAsync() { return OnInvokeAsync(); } UniTask IAsyncDeselectEventHandler.OnDeselectAsync() { return OnInvokeAsync(); } UniTask IAsyncSelectEventHandler.OnSelectAsync() { return OnInvokeAsync(); } UniTask IAsyncSubmitEventHandler.OnSubmitAsync() { return OnInvokeAsync(); } T IUniTaskSource.GetResult(short token) { try { return core.GetResult(token); } finally { if (callOnce) { Dispose(); } } } void IUniTaskSource.GetResult(short token) { ((IUniTaskSource)this).GetResult(token); } UniTaskStatus IUniTaskSource.GetStatus(short token) { return core.GetStatus(token); } UniTaskStatus IUniTaskSource.UnsafeGetStatus() { return core.UnsafeGetStatus(); } void IUniTaskSource.OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } } public class UnityEventHandlerAsyncEnumerable : IUniTaskAsyncEnumerable { readonly UnityEvent unityEvent; readonly CancellationToken cancellationToken1; public UnityEventHandlerAsyncEnumerable(UnityEvent unityEvent, CancellationToken cancellationToken) { this.unityEvent = unityEvent; this.cancellationToken1 = cancellationToken; } public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { if (this.cancellationToken1 == cancellationToken) { return new UnityEventHandlerAsyncEnumerator(unityEvent, this.cancellationToken1, CancellationToken.None); } else { return new UnityEventHandlerAsyncEnumerator(unityEvent, this.cancellationToken1, cancellationToken); } } class UnityEventHandlerAsyncEnumerator : MoveNextSource, IUniTaskAsyncEnumerator { static readonly Action cancel1 = OnCanceled1; static readonly Action cancel2 = OnCanceled2; readonly UnityEvent unityEvent; CancellationToken cancellationToken1; CancellationToken cancellationToken2; UnityAction unityAction; CancellationTokenRegistration registration1; CancellationTokenRegistration registration2; bool isDisposed; public UnityEventHandlerAsyncEnumerator(UnityEvent unityEvent, CancellationToken cancellationToken1, CancellationToken cancellationToken2) { this.unityEvent = unityEvent; this.cancellationToken1 = cancellationToken1; this.cancellationToken2 = cancellationToken2; } public AsyncUnit Current => default; public UniTask MoveNextAsync() { cancellationToken1.ThrowIfCancellationRequested(); cancellationToken2.ThrowIfCancellationRequested(); completionSource.Reset(); if (unityAction == null) { unityAction = Invoke; TaskTracker.TrackActiveTask(this, 3); unityEvent.AddListener(unityAction); if (cancellationToken1.CanBeCanceled) { registration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(cancel1, this); } if (cancellationToken2.CanBeCanceled) { registration2 = cancellationToken1.RegisterWithoutCaptureExecutionContext(cancel2, this); } } return new UniTask(this, completionSource.Version); } void Invoke() { completionSource.TrySetResult(true); } static void OnCanceled1(object state) { var self = (UnityEventHandlerAsyncEnumerator)state; self.DisposeAsync().Forget(); } static void OnCanceled2(object state) { var self = (UnityEventHandlerAsyncEnumerator)state; self.DisposeAsync().Forget(); } public UniTask DisposeAsync() { if (!isDisposed) { isDisposed = true; TaskTracker.RemoveTracking(this); registration1.Dispose(); registration2.Dispose(); unityEvent.RemoveListener(unityAction); } return default; } } } public class UnityEventHandlerAsyncEnumerable : IUniTaskAsyncEnumerable { readonly UnityEvent unityEvent; readonly CancellationToken cancellationToken1; public UnityEventHandlerAsyncEnumerable(UnityEvent unityEvent, CancellationToken cancellationToken) { this.unityEvent = unityEvent; this.cancellationToken1 = cancellationToken; } public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { if (this.cancellationToken1 == cancellationToken) { return new UnityEventHandlerAsyncEnumerator(unityEvent, this.cancellationToken1, CancellationToken.None); } else { return new UnityEventHandlerAsyncEnumerator(unityEvent, this.cancellationToken1, cancellationToken); } } class UnityEventHandlerAsyncEnumerator : MoveNextSource, IUniTaskAsyncEnumerator { static readonly Action cancel1 = OnCanceled1; static readonly Action cancel2 = OnCanceled2; readonly UnityEvent unityEvent; CancellationToken cancellationToken1; CancellationToken cancellationToken2; UnityAction unityAction; CancellationTokenRegistration registration1; CancellationTokenRegistration registration2; bool isDisposed; public UnityEventHandlerAsyncEnumerator(UnityEvent unityEvent, CancellationToken cancellationToken1, CancellationToken cancellationToken2) { this.unityEvent = unityEvent; this.cancellationToken1 = cancellationToken1; this.cancellationToken2 = cancellationToken2; } public T Current { get; private set; } public UniTask MoveNextAsync() { cancellationToken1.ThrowIfCancellationRequested(); cancellationToken2.ThrowIfCancellationRequested(); completionSource.Reset(); if (unityAction == null) { unityAction = Invoke; TaskTracker.TrackActiveTask(this, 3); unityEvent.AddListener(unityAction); if (cancellationToken1.CanBeCanceled) { registration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(cancel1, this); } if (cancellationToken2.CanBeCanceled) { registration2 = cancellationToken1.RegisterWithoutCaptureExecutionContext(cancel2, this); } } return new UniTask(this, completionSource.Version); } void Invoke(T value) { Current = value; completionSource.TrySetResult(true); } static void OnCanceled1(object state) { var self = (UnityEventHandlerAsyncEnumerator)state; self.DisposeAsync().Forget(); } static void OnCanceled2(object state) { var self = (UnityEventHandlerAsyncEnumerator)state; self.DisposeAsync().Forget(); } public UniTask DisposeAsync() { if (!isDisposed) { isDisposed = true; TaskTracker.RemoveTracking(this); registration1.Dispose(); registration2.Dispose(); if (unityEvent is IDisposable disp) { disp.Dispose(); } unityEvent.RemoveListener(unityAction); } return default; } } } } #endif