Files
XRLib/Assets/Scripts/UVC/UIToolkit/Modal/UTKToast.cs
2026-01-08 20:15:57 +09:00

275 lines
7.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#nullable enable
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit
{
/// <summary>
/// 토스트 알림 컴포넌트.
/// 화면 하단에 일시적으로 표시되는 알림입니다.
/// </summary>
[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
/// <summary>닫힘 이벤트</summary>
public event Action? OnClosed;
#endregion
#region Properties
/// <summary>메시지</summary>
[UxmlAttribute]
public string Message
{
get => _message;
set
{
_message = value;
if (_messageLabel != null) _messageLabel.text = value;
}
}
/// <summary>토스트 유형</summary>
[UxmlAttribute]
public ToastType Type
{
get => _type;
set
{
_type = value;
UpdateType();
}
}
/// <summary>표시 시간 (밀리초)</summary>
[UxmlAttribute]
public int Duration
{
get => _duration;
set => _duration = value;
}
/// <summary>닫기 버튼 표시 여부</summary>
[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<StyleSheet>(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
/// <summary>
/// Info 토스트 표시
/// </summary>
public static UTKToast ShowInfo(VisualElement parent, string message, int duration = DEFAULT_DURATION_MS)
{
return Show(parent, message, ToastType.Info, duration);
}
/// <summary>
/// Success 토스트 표시
/// </summary>
public static UTKToast ShowSuccess(VisualElement parent, string message, int duration = DEFAULT_DURATION_MS)
{
return Show(parent, message, ToastType.Success, duration);
}
/// <summary>
/// Warning 토스트 표시
/// </summary>
public static UTKToast ShowWarning(VisualElement parent, string message, int duration = DEFAULT_DURATION_MS)
{
return Show(parent, message, ToastType.Warning, duration);
}
/// <summary>
/// Error 토스트 표시
/// </summary>
public static UTKToast ShowError(VisualElement parent, string message, int duration = DEFAULT_DURATION_MS)
{
return Show(parent, message, ToastType.Error, duration);
}
/// <summary>
/// 토스트 표시
/// </summary>
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<ClickEvent>(_ => Close());
Add(_closeButton);
UpdateType();
}
private void SubscribeToThemeChanges()
{
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
RegisterCallback<DetachFromPanelEvent>(_ =>
{
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();
}
}
/// <summary>
/// 토스트 닫기
/// </summary>
public void Close()
{
OnClosed?.Invoke();
RemoveFromHierarchy();
}
#endregion
#region IDisposable
public void Dispose()
{
if (_disposed) return;
_disposed = true;
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnClosed = null;
}
#endregion
}
}