Files

181 lines
5.0 KiB
C#

using System;
using System.Threading;
using BriarQueen.Framework.Coordinators.Events;
using BriarQueen.Framework.Events.UI;
using BriarQueen.Framework.Managers.UI.Base;
using Cysharp.Threading.Tasks;
using PrimeTween;
using UnityEngine;
using UnityEngine.UI;
using VContainer;
namespace BriarQueen.UI
{
public class ScreenFader : MonoBehaviour, IScreenFader
{
private const float ALPHA_EPSILON = 0.001f;
[Header("UI Elements")]
[SerializeField]
private CanvasGroup _canvasGroup;
[Tooltip("Used for black / solid-color fades.")]
[SerializeField]
private Image _solidImage;
private Sequence _currentFadeSequence;
private EventCoordinator _eventCoordinator;
private CancellationTokenSource _fadeCts;
public bool IsModal => true;
private void Awake()
{
if (_canvasGroup == null)
Debug.LogError($"{nameof(ScreenFader)} on {name} is missing a CanvasGroup reference.", this);
SetInteractionState(_canvasGroup != null && _canvasGroup.alpha > ALPHA_EPSILON);
}
private void OnDestroy()
{
CancelAndDisposeFadeToken();
StopCurrentSequence();
}
// Window Stubs - Fader is Non-Interactive
public async UniTask Show()
{
await FadeToAsync( 1f);
}
public async UniTask Hide()
{
await FadeFromAsync(1f);
}
[Inject]
public void Construct(EventCoordinator eventCoordinator)
{
Debug.Log("ScreenFader constructed");
_eventCoordinator = eventCoordinator;
}
public async UniTask FadeToAsync(float duration)
{
if (_canvasGroup == null)
return;
_solidImage.color = Color.black;
BeginNewFade();
gameObject.SetActive(true);
SetInteractionState(true);
await FadeScreen(1f, duration, _fadeCts.Token);
}
public async UniTask FadeFromAsync(float duration)
{
if (_canvasGroup == null) return;
BeginNewFade();
await FadeScreen(0f, duration, _fadeCts.Token);
if (!_fadeCts.IsCancellationRequested)
{
_canvasGroup.alpha = 0f;
SetInteractionState(false);
}
}
public UniTask FadeToAsync(FadeStyle style, System.Drawing.Color tint, float duration)
{
throw new NotImplementedException();
}
private void ApplyTint(FadeStyle style, Color tint)
{
if (_solidImage != null)
{
_solidImage.enabled = true;
_solidImage.color = Color.black;
}
}
private async UniTask FadeScreen(float targetAlpha, float duration, CancellationToken token)
{
targetAlpha = Mathf.Clamp01(targetAlpha);
duration = Mathf.Max(0f, duration);
var currentAlpha = _canvasGroup.alpha;
if (Mathf.Abs(currentAlpha - targetAlpha) <= ALPHA_EPSILON)
{
_canvasGroup.alpha = targetAlpha;
SetInteractionState(targetAlpha > ALPHA_EPSILON);
return;
}
StopCurrentSequence();
if (duration <= 0f)
{
_canvasGroup.alpha = targetAlpha;
SetInteractionState(targetAlpha > ALPHA_EPSILON);
_eventCoordinator?.PublishImmediate(new FadeCompletedEvent());
return;
}
_currentFadeSequence = Sequence.Create()
.Group(Tween.Alpha(_canvasGroup, targetAlpha, duration, Ease.InOutSine));
try
{
await _currentFadeSequence.ToUniTask(cancellationToken: token);
_canvasGroup.alpha = targetAlpha;
SetInteractionState(targetAlpha > ALPHA_EPSILON);
_eventCoordinator?.PublishImmediate(new FadeCompletedEvent());
}
catch (OperationCanceledException)
{
// Another fade interrupted this one. Intentionally ignore completion.
}
}
private void BeginNewFade()
{
CancelAndDisposeFadeToken();
StopCurrentSequence();
_fadeCts = new CancellationTokenSource();
}
private void CancelAndDisposeFadeToken()
{
if (_fadeCts == null) return;
if (!_fadeCts.IsCancellationRequested) _fadeCts.Cancel();
_fadeCts.Dispose();
_fadeCts = null;
}
private void StopCurrentSequence()
{
if (_currentFadeSequence.isAlive) _currentFadeSequence.Stop();
}
private void SetInteractionState(bool isBlocking)
{
_canvasGroup.interactable = isBlocking;
_canvasGroup.blocksRaycasts = isBlocking;
}
}
}