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

333 lines
10 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 UnityEngine;
using UnityEngine.UIElements;
using UVC.UIToolkit.Modal;
namespace UVC.UIToolkit
{
/// <summary>
/// Alert 팝업 컴포넌트.
/// 사용자에게 중요한 정보를 알립니다.
/// </summary>
[UxmlElement]
public partial class UTKAlert : VisualElement, IDisposable
{
#region Constants
private const string USS_PATH = "UIToolkit/Modal/UTKAlert";
#endregion
#region Fields
private bool _disposed;
private UTKModalBlocker? _blocker;
private VisualElement? _iconContainer;
private Label? _iconLabel;
private Label? _titleLabel;
private Label? _messageLabel;
private VisualElement? _buttonContainer;
private string _title = "";
private string _message = "";
private AlertType _alertType = AlertType.Info;
#endregion
#region Events
/// <summary>닫힘 이벤트</summary>
public event Action? OnClosed;
/// <summary>확인 이벤트</summary>
public event Action? OnConfirm;
/// <summary>취소 이벤트</summary>
public event Action? OnCancel;
#endregion
#region Properties
/// <summary>제목</summary>
[UxmlAttribute]
public string Title
{
get => _title;
set
{
_title = value;
if (_titleLabel != null)
{
_titleLabel.text = value;
_titleLabel.style.display = string.IsNullOrEmpty(value) ? DisplayStyle.None : DisplayStyle.Flex;
}
}
}
/// <summary>메시지</summary>
[UxmlAttribute]
public string Message
{
get => _message;
set
{
_message = value;
if (_messageLabel != null) _messageLabel.text = value;
}
}
/// <summary>알림 유형</summary>
[UxmlAttribute]
public AlertType Type
{
get => _alertType;
set
{
_alertType = value;
UpdateType();
}
}
#endregion
#region Enums
public enum AlertType
{
Info,
Success,
Warning,
Error,
Confirm
}
#endregion
#region Constructor
public UTKAlert()
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
var uss = Resources.Load<StyleSheet>(USS_PATH);
if (uss != null)
{
styleSheets.Add(uss);
}
CreateUI();
SubscribeToThemeChanges();
}
public UTKAlert(string title, string message, AlertType type = AlertType.Info) : this()
{
Title = title;
Message = message;
Type = type;
}
#endregion
#region Static Factory
/// <summary>
/// Info 알림 표시
/// </summary>
public static UTKAlert ShowInfo(VisualElement parent, string title, string message, Action? onClose = null)
{
return Show(parent, title, message, AlertType.Info, onClose);
}
/// <summary>
/// Success 알림 표시
/// </summary>
public static UTKAlert ShowSuccess(VisualElement parent, string title, string message, Action? onClose = null)
{
return Show(parent, title, message, AlertType.Success, onClose);
}
/// <summary>
/// Warning 알림 표시
/// </summary>
public static UTKAlert ShowWarning(VisualElement parent, string title, string message, Action? onClose = null)
{
return Show(parent, title, message, AlertType.Warning, onClose);
}
/// <summary>
/// Error 알림 표시
/// </summary>
public static UTKAlert ShowError(VisualElement parent, string title, string message, Action? onClose = null)
{
return Show(parent, title, message, AlertType.Error, onClose);
}
/// <summary>
/// Confirm 대화상자 표시
/// </summary>
public static UTKAlert ShowConfirm(VisualElement parent, string title, string message, Action? onConfirm, Action? onCancel = null)
{
var alert = Show(parent, title, message, AlertType.Confirm, null);
alert.OnConfirm = onConfirm;
alert.OnCancel = onCancel;
return alert;
}
/// <summary>
/// 알림 표시
/// </summary>
public static UTKAlert Show(VisualElement parent, string title, string message, AlertType type, Action? onClose)
{
var alert = new UTKAlert(title, message, type);
alert.OnClosed = onClose;
alert._blocker = UTKModalBlocker.Show(parent, 0.5f, true);
alert._blocker.OnBlockerClicked += alert.Close;
alert._blocker.Add(alert);
// 중앙 정렬
alert.style.position = Position.Absolute;
alert.style.left = Length.Percent(50);
alert.style.top = Length.Percent(50);
alert.style.translate = new Translate(Length.Percent(-50), Length.Percent(-50));
return alert;
}
#endregion
#region UI Creation
private void CreateUI()
{
AddToClassList("utk-alert");
_iconContainer = new VisualElement { name = "icon-container" };
_iconContainer.AddToClassList("utk-alert__icon-container");
Add(_iconContainer);
_iconLabel = new Label { name = "icon" };
_iconLabel.AddToClassList("utk-alert__icon");
_iconContainer.Add(_iconLabel);
var contentContainer = new VisualElement { name = "content" };
contentContainer.AddToClassList("utk-alert__content");
Add(contentContainer);
_titleLabel = new Label { name = "title" };
_titleLabel.AddToClassList("utk-alert__title");
contentContainer.Add(_titleLabel);
_messageLabel = new Label { name = "message" };
_messageLabel.AddToClassList("utk-alert__message");
contentContainer.Add(_messageLabel);
_buttonContainer = new VisualElement { name = "buttons" };
_buttonContainer.AddToClassList("utk-alert__buttons");
Add(_buttonContainer);
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-alert--info");
RemoveFromClassList("utk-alert--success");
RemoveFromClassList("utk-alert--warning");
RemoveFromClassList("utk-alert--error");
RemoveFromClassList("utk-alert--confirm");
var typeClass = _alertType switch
{
AlertType.Success => "utk-alert--success",
AlertType.Warning => "utk-alert--warning",
AlertType.Error => "utk-alert--error",
AlertType.Confirm => "utk-alert--confirm",
_ => "utk-alert--info"
};
AddToClassList(typeClass);
// 아이콘 업데이트
if (_iconLabel != null)
{
_iconLabel.text = _alertType switch
{
AlertType.Success => "✓",
AlertType.Warning => "⚠",
AlertType.Error => "✕",
AlertType.Confirm => "?",
_ => ""
};
}
// 버튼 업데이트
UpdateButtons();
}
private void UpdateButtons()
{
if (_buttonContainer == null) return;
_buttonContainer.Clear();
if (_alertType == AlertType.Confirm)
{
var cancelBtn = new UTKButton("Cancel", "", UTKButton.ButtonVariant.Normal);
cancelBtn.AddToClassList("utk-alert__btn");
cancelBtn.OnClicked += () =>
{
OnCancel?.Invoke();
Close();
};
_buttonContainer.Add(cancelBtn);
var confirmBtn = new UTKButton("OK", "", UTKButton.ButtonVariant.Primary);
confirmBtn.AddToClassList("utk-alert__btn");
confirmBtn.OnClicked += () =>
{
OnConfirm?.Invoke();
Close();
};
_buttonContainer.Add(confirmBtn);
}
else
{
var okBtn = new UTKButton("OK", "", UTKButton.ButtonVariant.Primary);
okBtn.AddToClassList("utk-alert__btn");
okBtn.OnClicked += Close;
_buttonContainer.Add(okBtn);
}
}
/// <summary>
/// 알림 닫기
/// </summary>
public void Close()
{
OnClosed?.Invoke();
if (_blocker != null)
{
_blocker.OnBlockerClicked -= Close;
_blocker.Hide();
}
_blocker = null;
}
#endregion
#region IDisposable
public void Dispose()
{
if (_disposed) return;
_disposed = true;
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnClosed = null;
OnConfirm = null;
OnCancel = null;
_blocker = null;
}
#endregion
}
}