#nullable enable
using Cysharp.Threading.Tasks;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UVC.Extention;
using UVC.Log;
using UVC.Studio.Manager;
namespace UVC.UI.Modal
{
///
/// πΌοΈ λͺ¨λ¬ μ°½μ μ€μ 'λͺ¨μ΅'μ λ΄λΉνλ μΉκ΅¬μμ. Unity μλν°μμ λ§λ UI μμλ€(λ²νΌ, κΈμμ λ±)μ κ°μ§κ³ μμ΄μ.
/// `ModalContent`λΌλ λ μνΌλ₯Ό λ°μμ, κ·Έ λ΄μ©λλ‘ νλ©΄μ κ·Έλ¦Όμ κ·Έλ €μ£Όλ μν μ ν΄μ.
/// μ΄ μ€ν¬λ¦½νΈλ λͺ¨λ¬λ‘ μ¬μ©ν ν리νΉ(Prefab)μ κ°μ₯ λ°κΉ₯μͺ½ λΆλͺ¨ κ²μ μ€λΈμ νΈμ λΆμ¬μ€μΌ ν΄μ.
///
///
/// λ§μ½ μ
λ ₯μ°½μ΄ μλ νΉλ³ν λͺ¨λ¬μ λ§λ€κ³ μΆλ€λ©΄, μ΄ ModalViewλ₯Ό μμλ°μμ λ§λ€ μ μμ΄μ:
///
/// public class InputModalView : ModalView
/// {
/// [Header("My Special UI")]
/// public TMP_InputField myInputField; // Unity μλν°μμ μ°κ²°ν΄μ€μΌ ν΄μ.
/// private string _inputValue = "";
///
/// public override async UniTask OnOpen(ModalContent content)
/// {
/// await base.OnOpen(content); // λΆλͺ¨μ OnOpenμ λ¨Όμ νΈμΆν΄μ κΈ°λ³Έ UIλ₯Ό μ€μ ν΄μ.
/// ULog.Debug("μ
λ ₯ λͺ¨λ¬μ΄ μ΄λ Έμ΄μ! μ
λ ₯μ°½μ μ΄κΈ°νν©λλ€.");
/// if (myInputField != null)
/// {
/// myInputField.text = ""; // μ
λ ₯μ°½ λΉμ°κΈ°
/// // μ
λ ₯μ°½μ λ³νκ° μμ λλ§λ€ _inputValueλ₯Ό μ
λ°μ΄νΈνλλ‘ μ€μ ν μ μμ΄μ.
/// myInputField.OnValueChanged.AddListener(OnInputChanged);
/// }
/// }
///
/// private void OnInputChanged(string newValue)
/// {
/// _inputValue = newValue;
/// ULog.Debug($"μ
λ ₯λ κ°: {newValue}");
/// }
///
/// public override object GetResult()
/// {
/// // μ΄ λͺ¨λ¬μ΄ λ«ν λ, μ
λ ₯λ κΈμλ₯Ό κ²°κ³Όλ‘ λλ €μ€μ.
/// return _inputValue;
/// }
///
/// public override async UniTask OnClose(ModalContent content)
/// {
/// ULog.Debug("μ
λ ₯ λͺ¨λ¬μ΄ λ«νλλ€.");
/// if (myInputField != null)
/// {
/// myInputField.OnValueChanged.RemoveListener(OnInputChanged); // 리μ€λ μ 리
/// }
/// await base.OnClose(content);
/// }
/// }
///
/// // μ΄ InputModalViewλ₯Ό μ¬μ©νλ λ°©λ²:
/// // 1. InputModalView μ€ν¬λ¦½νΈκ° λΆμ΄μλ ν리νΉμ λ§λ€κ³ , myInputFieldλ₯Ό μ°κ²°ν΄μ.
/// // 2. ModalContent λ§λ€ λ μ΄ νλ¦¬νΉ κ²½λ‘λ₯Ό μ¬μ©ν΄μ.
/// // var inputContent = new ModalContent("Prefabs/UI/MyInputModalPrefab") { Title = "μ΄λ¦ μ
λ ₯", Message = "μ΄λ¦μ μ
λ ₯ν΄μ£ΌμΈμ." };
/// // string enteredName = await Modal.Open(inputContent);
/// // if (!string.IsNullOrEmpty(enteredName)) { ULog.Debug($"νμν©λλ€, {enteredName}λ!"); }
///
///
public class ModalView : MonoBehaviour
{
[Header("UI Elements")]
///
/// π·οΈ λͺ¨λ¬ μ°½μ μ λͺ©μ 보μ¬μ€ κΈμμ(TextMeshProUGUI)μμ.
/// Unity μλν°μ μΈμ€νν° μ°½μμ μ€μ UI μμλ₯Ό λμ΄λ€ μ°κ²°ν΄μ€μΌ ν΄μ.
///
[SerializeField]
public TextMeshProUGUI titleText;
///
/// π¬ λͺ¨λ¬ μ°½μ μ£Όμ λ©μμ§λ₯Ό 보μ¬μ€ κΈμμμμ. μ΄κ²λ μ°κ²°ν΄μ£ΌμΈμ!
///
[SerializeField]
public TextMeshProUGUI messageText;
///
/// β
'νμΈ' λ²νΌμ΄μμ. μ°κ²° νμ!
///
[SerializeField]
public Button confirmButton;
///
/// νμΈ λ²νΌ μμ μλ κΈμμμμ. νμΈ λ²νΌ κΈμλ₯Ό λ°κΏ λ μ¬μ©λΌμ.
///
[SerializeField]
public TextMeshProUGUI confirmButtonText;
///
/// β 'μ·¨μ' λ²νΌμ΄μμ. μ΄κ²λ μ°κ²°ν΄μ£ΌμΈμ!
///
[SerializeField]
public Button cancelButton;
///
/// μ·¨μ λ²νΌ μμ μλ κΈμμμμ. μ·¨μ λ²νΌ κΈμλ₯Ό λ°κΏ λ μ¬μ©λΌμ.
///
[SerializeField]
public TextMeshProUGUI cancelButtonText;
///
/// βοΈ λͺ¨λ¬ μ°½μ λ«λ (λ³΄ν΅ μ€λ₯Έμͺ½ μμ μλ X λͺ¨μ) λ²νΌμ΄μμ.
///
[SerializeField]
public Button closeButton; // λ«κΈ° λ²νΌ
// νμμ λ°λΌ λ€λ₯Έ UI μμλ€μ μΆκ°ν μ μμ΅λλ€.
// μ: public Image backgroundImage;
// μ: public InputField inputField;
///
/// π λͺ¨λ¬ μ°½μ΄ νλ©΄μ λνλ λ `Modal` ν΄λμ€κ° νΈμΆνλ λ§λ²μ΄μμ! (λΉλκΈ° μμ
κ°λ₯)
/// `ModalContent` λ μνΌλ₯Ό λ°μμ, μ΄ `ModalView`μ UI μμλ€(μ λͺ©, λ©μμ§, λ²νΌ λ±)μ λ μνΌλλ‘ μ€μ ν΄μ.
/// `ModalContent.OnOpen()`μ΄ νΈμΆλ *νμ* μ€νλΌμ.
///
/// λͺ¨λ¬μ 보μ¬μ€ λ΄μ©κ³Ό μ€μ μ λ΄μ 'λ μνΌ' (`ModalContent` κ°μ²΄)μμ.
public virtual async UniTask OnOpen(ModalContent content)
{
//ULog.Debug($"[ModalView] {gameObject.name} OnOpen called.");
// λͺ¨λ¬μ΄ μ΄λ¦¬λ©΄ λ¨μΆν€ λΉνμ±ν
if (ShortcutManager.Instance != null)
{
ShortcutManager.Instance.GlobalEnabled = false;
}
// ModalContent λ μνΌμ μ ν λλ‘ UI μμλ€μ μ€μ ν΄μ.
if (titleText != null && content != null)
{
titleText.text = content.Title;
}
if (messageText != null && content != null)
{
messageText.text = content.Message;
}
// νμΈ λ²νΌ μ€μ
if (confirmButton != null && content != null)
{
confirmButton.gameObject.SetActive(content.ShowConfirmButton);
if (content.ShowConfirmButton && confirmButtonText != null && !string.IsNullOrEmpty(content.ConfirmButtonText))
{
confirmButtonText.text = content.ConfirmButtonText;
}
}
// μ·¨μ λ²νΌ μ€μ
if (cancelButton != null && content != null)
{
cancelButton.gameObject.SetActive(content.ShowCancelButton);
if (content.ShowCancelButton && cancelButtonText != null && !string.IsNullOrEmpty(content.CancelButtonText))
{
cancelButtonText.text = content.CancelButtonText;
}
}
// λ«κΈ° λ²νΌ μ€μ
if (closeButton != null && content != null)
{
closeButton.gameObject.SetActive(content.ShowCloseButton);
}
AdjustPositions();
await UniTask.CompletedTask; // λΉλκΈ° λ©μλλΌμ λ§μ§λ§μ μ΄κ±Έ λΆμ¬μ€μ.
}
///
/// π νμ±νλ Textμ buttonμ΄ Layout Groupμ μ¬μ©νκ³ μμΌλ©΄ μλ μ‘°μ λκ² μ€μ ν©λλ€.
///
protected virtual void AdjustPositions()
{
}
///
/// π¬ λͺ¨λ¬ μ°½μ΄ νλ©΄μμ μ¬λΌμ§ λ `Modal` ν΄λμ€κ° νΈμΆνλ λ§λ²μ΄μμ! (λΉλκΈ° μμ
κ°λ₯)
/// `ModalContent.OnClose()`κ° νΈμΆλ *νμ* μ€νλΌμ.
/// λͺ¨λ¬μ΄ λ«νλ©΄μ νΉλ³ν μ 리ν΄μΌ ν μμ
μ΄ μλ€λ©΄ μ¬κΈ°μ μμ±ν΄μ.
///
/// μ΄ λͺ¨λ¬μ μ΄ λ μ¬μ©νλ 'λ μνΌ' (`ModalContent` κ°μ²΄)μμ.
public virtual async UniTask OnClose(ModalContent content)
{
//ULog.Debug($"[ModalView] {gameObject.name} OnClose called.");
// λͺ¨λ¬μ΄ λ«νλ©΄ λ¨μΆν€ λ€μ νμ±ν
if (ShortcutManager.Instance != null)
{
ShortcutManager.Instance.GlobalEnabled = true;
}
// μ: λͺ¨λ¬μμ μ¬μ©νλ 리μμ€λ₯Ό ν΄μ νκ±°λ, UI μνλ₯Ό μ΄κΈ°ννλ μ½λλ₯Ό μ¬κΈ°μ λ£μ μ μμ΄μ.
await UniTask.CompletedTask;
}
///
/// π λͺ¨λ¬ μ°½μ΄ λ«ν λ, μ΄ λͺ¨λ¬μ΄ μ΄λ€ 'κ²°κ³Ό'λ₯Ό λ§λ€μλμ§ μλ €μ£Όλ ν¨μμμ.
/// κΈ°λ³Έμ μΌλ‘λ μ무κ²λ μ μλ €μ€μ (`null` λ°ν).
/// λ§μ½ λͺ¨λ¬μμ μ¬μ©μκ° λκ°λ₯Ό μ ννκ±°λ μ
λ ₯νλ€λ©΄, μ΄ ν¨μλ₯Ό **μ¬μ μ(override)**ν΄μ
/// κ·Έ μ ν/μ
λ ₯ κ°μ λλ €μ£Όλλ‘ λ§λ€ μ μμ΄μ.
/// `Modal.Open()`λ₯Ό νΈμΆν λ `T`μ μ§μ ν νμ
μΌλ‘ μ΄ κ²°κ³Όκ° λ³νλΌμ.
///
/// λͺ¨λ¬μ μ²λ¦¬ κ²°κ³Όμμ. (μ: μ¬μ©μκ° μ
λ ₯ν κΈμ, μ νν μμ΄ν
, λλ κ·Έλ₯ true/false)
///
/// μλ₯Ό λ€μ΄, 'μ'/'μλμ€'λ₯Ό μ ννλ κ°λ¨ν νμΈ λͺ¨λ¬μ΄λΌλ©΄ μ΄λ κ² ν μ μμ΄μ:
///
/// public class ConfirmModalView : ModalView
/// {
/// private bool _wasConfirmed = false;
///
/// // (OnOpen λ± λ€λ₯Έ λ©μλλ€μ νμμ λ°λΌ ꡬν)
///
/// // νμΈ λ²νΌμ΄ λλ Έμ λ νΈμΆλ λ©μλ (Modal.csμμ μ°κ²°ν΄μ€)
/// public void HandleConfirm()
/// {
/// _wasConfirmed = true;
/// // μ€μ λ‘λ Modal.csμ HandleModalActionAsyncκ° νΈμΆλμ΄ λͺ¨λ¬μ΄ λ«νλλ€.
/// }
///
/// // μ·¨μ λ²νΌμ΄ λλ Έμ λ νΈμΆλ λ©μλ
/// public void HandleCancel()
/// {
/// _wasConfirmed = false;
/// }
///
/// public override object GetResult()
/// {
/// // μ¬μ©μκ° 'νμΈ'μ λλ λμ§ μ¬λΆλ₯Ό bool κ°μΌλ‘ λ°νν΄μ.
/// return _wasConfirmed;
/// }
/// }
/// // Modal.Open(...) μ΄λ κ² νΈμΆνλ©΄, GetResult()κ° λ°νν bool κ°μ λ°μ μ μμ΄μ.
///
/// λλ, μ
λ ₯ νλκ° μλ λͺ¨λ¬μ΄λΌλ©΄ μ
λ ₯λ ν
μ€νΈλ₯Ό λ°νν μ μμ΄μ:
///
/// public class InputModalView : ModalView
/// {
/// public TMP_InputField inputField;
/// // (OnOpenμμ inputField μ΄κΈ°ν λ° μ΄λ²€νΈ μ°κ²°)
///
/// public override UniTask
///
public virtual UniTask