#nullable enable
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit
{
///
/// 토스트 알림 컴포넌트.
/// 화면 하단에 일시적으로 표시되는 알림입니다.
///
[UxmlElement]
public partial class UTKToast : VisualElement, IDisposable
{
#region Constants
private const string USS_PATH = "UIToolkit/Modal/UTKToast";
private const int DEFAULT_DURATION_MS = 3000;
#endregion
#region Fields
private bool _disposed;
private Label? _iconLabel;
private Label? _messageLabel;
private Button? _closeButton;
private string _message = "";
private ToastType _type = ToastType.Info;
private int _duration = DEFAULT_DURATION_MS;
private bool _showCloseButton;
#endregion
#region Events
/// 닫힘 이벤트
public event Action? OnClosed;
#endregion
#region Properties
/// 메시지
[UxmlAttribute]
public string Message
{
get => _message;
set
{
_message = value;
if (_messageLabel != null) _messageLabel.text = value;
}
}
/// 토스트 유형
[UxmlAttribute]
public ToastType Type
{
get => _type;
set
{
_type = value;
UpdateType();
}
}
/// 표시 시간 (밀리초)
[UxmlAttribute]
public int Duration
{
get => _duration;
set => _duration = value;
}
/// 닫기 버튼 표시 여부
[UxmlAttribute]
public bool ShowCloseButton
{
get => _showCloseButton;
set
{
_showCloseButton = value;
if (_closeButton != null)
{
_closeButton.style.display = value ? DisplayStyle.Flex : DisplayStyle.None;
}
}
}
#endregion
#region Enums
public enum ToastType
{
Info,
Success,
Warning,
Error
}
#endregion
#region Constructor
public UTKToast()
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
var uss = Resources.Load(USS_PATH);
if (uss != null)
{
styleSheets.Add(uss);
}
CreateUI();
SubscribeToThemeChanges();
}
public UTKToast(string message, ToastType type = ToastType.Info) : this()
{
Message = message;
Type = type;
}
#endregion
#region Static Factory
///
/// Info 토스트 표시
///
public static UTKToast ShowInfo(VisualElement parent, string message, int duration = DEFAULT_DURATION_MS)
{
return Show(parent, message, ToastType.Info, duration);
}
///
/// Success 토스트 표시
///
public static UTKToast ShowSuccess(VisualElement parent, string message, int duration = DEFAULT_DURATION_MS)
{
return Show(parent, message, ToastType.Success, duration);
}
///
/// Warning 토스트 표시
///
public static UTKToast ShowWarning(VisualElement parent, string message, int duration = DEFAULT_DURATION_MS)
{
return Show(parent, message, ToastType.Warning, duration);
}
///
/// Error 토스트 표시
///
public static UTKToast ShowError(VisualElement parent, string message, int duration = DEFAULT_DURATION_MS)
{
return Show(parent, message, ToastType.Error, duration);
}
///
/// 토스트 표시
///
public static UTKToast Show(VisualElement parent, string message, ToastType type, int duration = DEFAULT_DURATION_MS)
{
var toast = new UTKToast(message, type);
toast._duration = duration;
parent.Add(toast);
// 하단 중앙 정렬
toast.style.position = Position.Absolute;
toast.style.left = Length.Percent(50);
toast.style.bottom = 20;
toast.style.translate = new Translate(Length.Percent(-50), 0);
// 자동 닫기
if (duration > 0)
{
toast.AutoCloseAsync(duration).Forget();
}
return toast;
}
#endregion
#region UI Creation
private void CreateUI()
{
AddToClassList("utk-toast");
_iconLabel = new Label { name = "icon" };
_iconLabel.AddToClassList("utk-toast__icon");
Add(_iconLabel);
_messageLabel = new Label { name = "message" };
_messageLabel.AddToClassList("utk-toast__message");
Add(_messageLabel);
_closeButton = new Button { name = "close-btn", text = "✕" };
_closeButton.AddToClassList("utk-toast__close-btn");
_closeButton.style.display = DisplayStyle.None;
_closeButton.RegisterCallback(_ => Close());
Add(_closeButton);
UpdateType();
}
private void SubscribeToThemeChanges()
{
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
RegisterCallback(_ =>
{
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
});
}
private void OnThemeChanged(UTKTheme theme)
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
}
#endregion
#region Methods
private void UpdateType()
{
RemoveFromClassList("utk-toast--info");
RemoveFromClassList("utk-toast--success");
RemoveFromClassList("utk-toast--warning");
RemoveFromClassList("utk-toast--error");
var typeClass = _type switch
{
ToastType.Success => "utk-toast--success",
ToastType.Warning => "utk-toast--warning",
ToastType.Error => "utk-toast--error",
_ => "utk-toast--info"
};
AddToClassList(typeClass);
if (_iconLabel != null)
{
_iconLabel.text = _type switch
{
ToastType.Success => "✓",
ToastType.Warning => "⚠",
ToastType.Error => "✕",
_ => "ℹ"
};
}
}
private async UniTaskVoid AutoCloseAsync(int delayMs)
{
await UniTask.Delay(delayMs);
if (!_disposed)
{
Close();
}
}
///
/// 토스트 닫기
///
public void Close()
{
OnClosed?.Invoke();
RemoveFromHierarchy();
}
#endregion
#region IDisposable
public void Dispose()
{
if (_disposed) return;
_disposed = true;
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnClosed = null;
}
#endregion
}
}