First commit for private source control. Older commits available on Github.
This commit is contained in:
341
Assets/Scripts/UI/Menus/ConfirmDeleteWindow.cs
Normal file
341
Assets/Scripts/UI/Menus/ConfirmDeleteWindow.cs
Normal file
@@ -0,0 +1,341 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using BriarQueen.UI.Menus.Components;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using PrimeTween;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace BriarQueen.UI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Confirm delete modal with tweened show/hide (fade + scale).
|
||||
/// - Open(): animates in
|
||||
/// - Close(): animates out
|
||||
/// - CloseImmediate(): hard hide (safe for OnDisable/OnDestroy)
|
||||
/// Notes:
|
||||
/// - Uses unscaled time so it still animates while paused.
|
||||
/// - Gates input during transitions.
|
||||
/// </summary>
|
||||
public class ConfirmDeleteWindow : MonoBehaviour
|
||||
{
|
||||
[Header("Root")]
|
||||
[SerializeField]
|
||||
private GameObject _root;
|
||||
|
||||
[Header("Animation Targets")]
|
||||
[SerializeField]
|
||||
private CanvasGroup _canvasGroup;
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform _panelTransform;
|
||||
|
||||
[Header("UI")]
|
||||
[SerializeField]
|
||||
private TextMeshProUGUI _titleText;
|
||||
|
||||
[SerializeField]
|
||||
private Button _confirmButton;
|
||||
|
||||
[SerializeField]
|
||||
private Button _cancelButton;
|
||||
|
||||
[Header("Tween Settings")]
|
||||
[SerializeField]
|
||||
private float _duration = 0.18f;
|
||||
|
||||
[SerializeField]
|
||||
private Ease _easeIn = Ease.OutBack;
|
||||
|
||||
[SerializeField]
|
||||
private Ease _easeOut = Ease.InQuad;
|
||||
|
||||
[SerializeField]
|
||||
private bool _useUnscaledTime = true;
|
||||
|
||||
[Header("Scale")]
|
||||
[SerializeField]
|
||||
private float _fromScale = 0.92f;
|
||||
|
||||
[SerializeField]
|
||||
private float _toScale = 1.0f;
|
||||
|
||||
private CancellationTokenSource _cts;
|
||||
private bool _isAnimating;
|
||||
|
||||
private bool _isOpen;
|
||||
|
||||
private SaveFileInfo _pending;
|
||||
|
||||
private Sequence _sequence;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_confirmButton != null) _confirmButton.onClick.AddListener(Confirm);
|
||||
if (_cancelButton != null) _cancelButton.onClick.AddListener(Cancel);
|
||||
|
||||
CloseImmediate();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_confirmButton != null) _confirmButton.onClick.RemoveListener(Confirm);
|
||||
if (_cancelButton != null) _cancelButton.onClick.RemoveListener(Cancel);
|
||||
|
||||
StopAnim();
|
||||
}
|
||||
|
||||
public event Action<SaveFileInfo> OnConfirmDelete;
|
||||
public event Action OnCancel;
|
||||
|
||||
public void Open(SaveFileInfo info)
|
||||
{
|
||||
_pending = info;
|
||||
|
||||
if (_titleText != null)
|
||||
_titleText.text = $"Delete '{info.FileName}'?";
|
||||
|
||||
OpenAsync().Forget();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
CloseAsync().Forget();
|
||||
}
|
||||
|
||||
public void CloseImmediate()
|
||||
{
|
||||
StopAnim();
|
||||
|
||||
_pending = default;
|
||||
_isOpen = false;
|
||||
_isAnimating = false;
|
||||
|
||||
if (_root != null) _root.SetActive(false);
|
||||
|
||||
if (_canvasGroup != null)
|
||||
{
|
||||
_canvasGroup.alpha = 0f;
|
||||
_canvasGroup.interactable = false;
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
}
|
||||
|
||||
if (_panelTransform != null)
|
||||
{
|
||||
var s = _toScale;
|
||||
_panelTransform.localScale = new Vector3(s, s, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask OpenAsync()
|
||||
{
|
||||
if (_isOpen && !_isAnimating) return;
|
||||
|
||||
EnsureRefs();
|
||||
|
||||
StopAnim();
|
||||
_cts = new CancellationTokenSource();
|
||||
var token = _cts.Token;
|
||||
|
||||
if (_root != null) _root.SetActive(true);
|
||||
|
||||
_isAnimating = true;
|
||||
|
||||
// Prep start state
|
||||
if (_canvasGroup != null)
|
||||
{
|
||||
_canvasGroup.alpha = 0f;
|
||||
_canvasGroup.interactable = false;
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
}
|
||||
|
||||
if (_panelTransform != null)
|
||||
{
|
||||
var s = _fromScale;
|
||||
_panelTransform.localScale = new Vector3(s, s, 1f);
|
||||
}
|
||||
|
||||
// Build sequence (fade + scale)
|
||||
_sequence = Sequence.Create(useUnscaledTime: true);
|
||||
|
||||
if (_canvasGroup != null)
|
||||
_sequence.Group(Tween.Alpha(_canvasGroup, new TweenSettings<float>
|
||||
{
|
||||
startValue = 0f,
|
||||
endValue = 1f,
|
||||
settings = new TweenSettings
|
||||
{
|
||||
duration = _duration,
|
||||
ease = _easeIn,
|
||||
useUnscaledTime = _useUnscaledTime
|
||||
}
|
||||
}));
|
||||
|
||||
if (_panelTransform != null)
|
||||
_sequence.Group(Tween.Scale(_panelTransform, new TweenSettings<Vector3>
|
||||
{
|
||||
startValue = new Vector3(_fromScale, _fromScale, 1f),
|
||||
endValue = new Vector3(_toScale, _toScale, 1f),
|
||||
settings = new TweenSettings
|
||||
{
|
||||
duration = _duration,
|
||||
ease = _easeIn,
|
||||
useUnscaledTime = _useUnscaledTime
|
||||
}
|
||||
}));
|
||||
|
||||
try
|
||||
{
|
||||
await _sequence.ToUniTask(cancellationToken: token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sequence = default;
|
||||
_isAnimating = false;
|
||||
_isOpen = true;
|
||||
}
|
||||
|
||||
// Enable input once fully open
|
||||
if (_canvasGroup != null)
|
||||
{
|
||||
_canvasGroup.alpha = 1f;
|
||||
_canvasGroup.interactable = true;
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
}
|
||||
|
||||
// Optional: focus cancel for controller/keyboard flows
|
||||
_cancelButton?.Select();
|
||||
}
|
||||
|
||||
private async UniTask CloseAsync()
|
||||
{
|
||||
if (!_isOpen && !_isAnimating) return;
|
||||
|
||||
EnsureRefs();
|
||||
|
||||
StopAnim();
|
||||
_cts = new CancellationTokenSource();
|
||||
var token = _cts.Token;
|
||||
|
||||
_isAnimating = true;
|
||||
|
||||
// Disable input immediately while animating out
|
||||
if (_canvasGroup != null)
|
||||
{
|
||||
_canvasGroup.interactable = false;
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
}
|
||||
|
||||
var startAlpha = _canvasGroup != null ? _canvasGroup.alpha : 1f;
|
||||
var startScale = _panelTransform != null ? _panelTransform.localScale : Vector3.one;
|
||||
|
||||
_sequence = Sequence.Create(useUnscaledTime: true);
|
||||
|
||||
if (_canvasGroup != null)
|
||||
_sequence.Group(Tween.Alpha(_canvasGroup, new TweenSettings<float>
|
||||
{
|
||||
startValue = startAlpha,
|
||||
endValue = 0f,
|
||||
settings = new TweenSettings
|
||||
{
|
||||
duration = _duration,
|
||||
ease = _easeOut,
|
||||
useUnscaledTime = _useUnscaledTime
|
||||
}
|
||||
}));
|
||||
|
||||
if (_panelTransform != null)
|
||||
_sequence.Group(Tween.Scale(_panelTransform, new TweenSettings<Vector3>
|
||||
{
|
||||
startValue = startScale,
|
||||
endValue = new Vector3(_fromScale, _fromScale, 1f),
|
||||
settings = new TweenSettings
|
||||
{
|
||||
duration = _duration,
|
||||
ease = _easeOut,
|
||||
useUnscaledTime = _useUnscaledTime
|
||||
}
|
||||
}));
|
||||
|
||||
try
|
||||
{
|
||||
await _sequence.ToUniTask(cancellationToken: token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sequence = default;
|
||||
_isAnimating = false;
|
||||
_isOpen = false;
|
||||
}
|
||||
|
||||
// Fully hidden
|
||||
if (_canvasGroup != null) _canvasGroup.alpha = 0f;
|
||||
if (_root != null) _root.SetActive(false);
|
||||
|
||||
_pending = default;
|
||||
}
|
||||
|
||||
private void Confirm()
|
||||
{
|
||||
if (_isAnimating) return;
|
||||
|
||||
var info = _pending;
|
||||
Close();
|
||||
OnConfirmDelete?.Invoke(info);
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
if (_isAnimating) return;
|
||||
|
||||
Close();
|
||||
OnCancel?.Invoke();
|
||||
}
|
||||
|
||||
private void StopAnim()
|
||||
{
|
||||
if (_sequence.isAlive)
|
||||
{
|
||||
_sequence.Stop();
|
||||
_sequence = default;
|
||||
}
|
||||
|
||||
if (_cts != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_cts.Cancel();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
_cts.Dispose();
|
||||
_cts = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureRefs()
|
||||
{
|
||||
// If you forgot to wire these, try to find sensible defaults.
|
||||
if (_canvasGroup == null && _root != null)
|
||||
_canvasGroup = _root.GetComponent<CanvasGroup>();
|
||||
|
||||
if (_panelTransform == null)
|
||||
_panelTransform = GetComponentInChildren<RectTransform>(true);
|
||||
|
||||
// Root fallback: if none specified, use this GO
|
||||
if (_root == null)
|
||||
_root = gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user