#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6)) #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member using System; using System.Runtime.CompilerServices; using System.Threading; using UniRx.Async.Internal; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using UniRx.Async.Triggers; namespace UniRx.Async { 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 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 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 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 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 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 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 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 OnValueChanged(this Dropdown dropdown) { return new AsyncUnityEventHandler(dropdown.onValueChanged, dropdown.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); } public static UniTask OnValueChanged(this Dropdown dropdown, CancellationToken cancellationToken) { return new AsyncUnityEventHandler(dropdown.onValueChanged, cancellationToken, true).OnInvokeAsync(); } } public interface IAsyncClickEventHandler : IDisposable { UniTask OnClickAsync(); UniTask OnClickAsyncSuppressCancellationThrow(); } public interface IAsyncValueChangedEventHandler : IDisposable { UniTask OnValueChangedAsync(); UniTask<(bool IsCanceled, T Result)> OnValueChangedAsyncSuppressCancellationThrow(); } public interface IAsyncEndEditEventHandler : IDisposable { UniTask OnEndEditAsync(); UniTask<(bool IsCanceled, T Result)> OnEndEditAsyncSuppressCancellationThrow(); } // event handler is reusable when callOnce = false. public class AsyncUnityEventHandler : IAwaiter, IDisposable, IAsyncClickEventHandler { static Action cancellationCallback = CancellationCallback; readonly UnityAction action; readonly UnityEvent unityEvent; Action continuation; CancellationTokenRegistration registration; bool isDisposed; bool callOnce; UniTask? suppressCancellationThrowTask; public AsyncUnityEventHandler(UnityEvent unityEvent, CancellationToken cancellationToken, bool callOnce) { this.callOnce = callOnce; if (cancellationToken.IsCancellationRequested) { isDisposed = true; return; } action = Invoke; unityEvent.AddListener(action); this.unityEvent = unityEvent; if (cancellationToken.CanBeCanceled) { registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); } TaskTracker.TrackActiveTask(this, 3); } public UniTask OnInvokeAsync() { // zero allocation wait handler. return new UniTask(this); } public UniTask OnInvokeAsyncSuppressCancellationThrow() { if (suppressCancellationThrowTask == null) { suppressCancellationThrowTask = OnInvokeAsync().SuppressCancellationThrow(); } return suppressCancellationThrowTask.Value; } void Invoke() { var c = continuation; continuation = null; if (c != null) { c.Invoke(); } } static void CancellationCallback(object state) { var self = (AsyncUnityEventHandler)state; self.Dispose(); self.Invoke(); // call continuation if exists yet(GetResult -> throw OperationCanceledException). } public void Dispose() { if (!isDisposed) { isDisposed = true; TaskTracker.RemoveTracking(this); registration.Dispose(); if (unityEvent != null) { unityEvent.RemoveListener(action); } } } bool IAwaiter.IsCompleted => isDisposed ? true : false; AwaiterStatus IAwaiter.Status => isDisposed ? AwaiterStatus.Canceled : AwaiterStatus.Pending; void IAwaiter.GetResult() { if (isDisposed) throw new OperationCanceledException(); if (callOnce) Dispose(); } void INotifyCompletion.OnCompleted(Action action) { ((ICriticalNotifyCompletion)this).UnsafeOnCompleted(action); } void ICriticalNotifyCompletion.UnsafeOnCompleted(Action action) { Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); this.continuation = action; } // Interface events. UniTask IAsyncClickEventHandler.OnClickAsync() { return OnInvokeAsync(); } UniTask IAsyncClickEventHandler.OnClickAsyncSuppressCancellationThrow() { return OnInvokeAsyncSuppressCancellationThrow(); } } // event handler is reusable when callOnce = false. public class AsyncUnityEventHandler : IAwaiter, IDisposable, IAsyncValueChangedEventHandler, IAsyncEndEditEventHandler { static Action cancellationCallback = CancellationCallback; readonly UnityAction action; readonly UnityEvent unityEvent; Action continuation; CancellationTokenRegistration registration; bool isDisposed; T eventValue; bool callOnce; UniTask<(bool, T)>? suppressCancellationThrowTask; public AsyncUnityEventHandler(UnityEvent unityEvent, CancellationToken cancellationToken, bool callOnce) { this.callOnce = callOnce; if (cancellationToken.IsCancellationRequested) { isDisposed = true; return; } action = Invoke; unityEvent.AddListener(action); this.unityEvent = unityEvent; if (cancellationToken.CanBeCanceled) { registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); } TaskTracker.TrackActiveTask(this, 3); } public UniTask OnInvokeAsync() { // zero allocation wait handler. return new UniTask(this); } public UniTask<(bool IsCanceled, T Result)> OnInvokeAsyncSuppressCancellationThrow() { if (suppressCancellationThrowTask == null) { suppressCancellationThrowTask = OnInvokeAsync().SuppressCancellationThrow(); } return suppressCancellationThrowTask.Value; } void Invoke(T value) { this.eventValue = value; var c = continuation; continuation = null; if (c != null) { c.Invoke(); } } static void CancellationCallback(object state) { var self = (AsyncUnityEventHandler)state; self.Dispose(); self.Invoke(default(T)); // call continuation if exists yet(GetResult -> throw OperationCanceledException). } public void Dispose() { if (!isDisposed) { isDisposed = true; TaskTracker.RemoveTracking(this); registration.Dispose(); if (unityEvent != null) { unityEvent.RemoveListener(action); } } } bool IAwaiter.IsCompleted => isDisposed ? true : false; AwaiterStatus IAwaiter.Status => isDisposed ? AwaiterStatus.Canceled : AwaiterStatus.Pending; T IAwaiter.GetResult() { if (isDisposed) throw new OperationCanceledException(); if (callOnce) Dispose(); return eventValue; } void IAwaiter.GetResult() { if (isDisposed) throw new OperationCanceledException(); if (callOnce) Dispose(); } void INotifyCompletion.OnCompleted(Action action) { ((ICriticalNotifyCompletion)this).UnsafeOnCompleted(action); } void ICriticalNotifyCompletion.UnsafeOnCompleted(Action action) { Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); this.continuation = action; } // Interface events. UniTask IAsyncValueChangedEventHandler.OnValueChangedAsync() { return OnInvokeAsync(); } UniTask<(bool IsCanceled, T Result)> IAsyncValueChangedEventHandler.OnValueChangedAsyncSuppressCancellationThrow() { return OnInvokeAsyncSuppressCancellationThrow(); } UniTask IAsyncEndEditEventHandler.OnEndEditAsync() { return OnInvokeAsync(); } UniTask<(bool IsCanceled, T Result)> IAsyncEndEditEventHandler.OnEndEditAsyncSuppressCancellationThrow() { return OnInvokeAsyncSuppressCancellationThrow(); } } } #endif