using System.Threading; using KitsuneCafe.System; using KitsuneCafe.System.Attributes; using R3; using UnityEngine; using UnityEngine.UIElements; using KitsuneCafe.Extension; using Unit = R3.Unit; namespace KitsuneCafe.UI { [CreateAssetMenu(menuName = KitsuneCafeMenu.UiEffect + "Fade")] public class FadeEffect : BaseUiEffect { [SerializeField] private bool addTransition = false; [SerializeField, ShowIf("addTransition")] private string propertyName = "opacity"; [SerializeField, ShowIf("addTransition")] private SerializableDuration duration; [SerializeField, ShowIf("addTransition")] private EasingMode easing; [SerializeField, ShowIf("addTransition")] private SerializableDuration delay; [SerializeField, Range(0f, 1f)] private float from; [SerializeField, Range(0f, 1f)] private float to; public override IUiEffect Instantiate() { var fade = new FadeEffectInstance(from, to); if (addTransition) { return new AddTransitionEffect(propertyName, duration, easing, delay, fade); } return fade; } } public readonly struct FadeEffectInstance : IUiEffect { public readonly float From; public readonly float To; public FadeEffectInstance(float from, float to) { From = from; To = to; } private Observable ObserveGeometryChange(VisualElement target, CancellationToken token) { return target.ObserveEvent(token) .OnErrorResumeAsFailure() .AsUnitObservable(); } private Observable Defer(VisualElement target, CancellationToken token) { return Observable.Merge( ObserveGeometryChange(target, token), Observable.EveryUpdate(UnityFrameProvider.PostLateUpdate, token) ) .Where(_ => target.resolvedStyle.width > 0 && target.resolvedStyle.height > 0) .Take(1); } public Observable Execute(VisualElement target, CancellationToken token) { target.style.opacity = From; var to = To; return Defer(target, token) .Do(_ => target.style.opacity = to) .Select(_ => target.ObserveEvent()) .Switch() .Where(evt => evt.stylePropertyNames.Contains("opacity")) .Take(1) .TakeUntil(token) .AsUnitObservable(); } } }