Files
XRLib/Assets/Scripts/UVC/UI/Modal/DatePicker/DatePicker.cs

247 lines
9.0 KiB
C#
Raw Normal View History

2025-09-22 20:12:56 +09:00
using Cysharp.Threading.Tasks;
using DG.Tweening;
using System;
using System.Collections.Generic;
using TMPro;
using UnityEditor.Search;
using UnityEngine;
using UVC.Log;
using UVC.Util;
namespace UVC.UI.Modal.DatePicker
{
public class DatePicker : MonoBehaviour
{
private static string PrefabPath = "Prefabs/UI/Modal/DatePicker/DatePicker";
private const string blockerPrefabPath = "Prefabs/UI/Modal/ModalBlocker"; //menu blocker prefab path
private static DatePicker instance;
private static Action<DateTime> _onDateSelectedCallback;
private Canvas parentCanvas;
private static GameObject? blockerPrefabObj;
private static GameObject currentBlockerInstance;
[Header("Calendar")]
[SerializeField]
public GameObject _calendarPanel;
[SerializeField]
public TextMeshProUGUI _yearNumText;
[SerializeField]
public TextMeshProUGUI _monthNumText;
[SerializeField]
public GameObject _item;
public List<GameObject> _dateItems = new List<GameObject>();
const int _totalDateNum = 42;
private DateTime _dateTime;
public static async void Show(DateTime initialDate, Action<DateTime> onDateSelected)
{
if (instance == null)
{
var prefab = await Resources.LoadAsync<DatePicker>(PrefabPath) as DatePicker;
instance = Instantiate(prefab);
if (instance == null)
{
Debug.LogError("No DatePicker prefab active on 'Start' in scene");
return;
}
Canvas[] canvases = FindObjectsByType<Canvas>(FindObjectsSortMode.None);
//sortingOrder로 정렬 내림차순 - sortingOrder가 높은 캔버스가 먼저 오도록
Array.Sort(canvases, (x, y) => y.sortingOrder.CompareTo(x.sortingOrder));
foreach (Canvas c in canvases)
{
if (c.renderMode != RenderMode.WorldSpace)
{
instance.transform.SetParent(c.transform, false);
instance.parentCanvas = c;
break;
}
}
}
instance.gameObject.SetActive(true);
instance._dateTime = initialDate;
//화면 센터에 위치 시키기
RectTransform rectTransform = instance.GetComponent<RectTransform>();
rectTransform.anchoredPosition = new Vector2((Screen.width - rectTransform.sizeDelta.x) / 2, -(Screen.height - rectTransform.sizeDelta.y) / 2);
_onDateSelectedCallback = onDateSelected;
ShowBlock();
}
public static void Hide()
{
if (instance != null) instance.gameObject.SetActive(false);
_onDateSelectedCallback = null; // 콜백 참조 정리
HideBlock();
}
private static async void ShowBlock()
{
if (blockerPrefabObj == null)
{
blockerPrefabObj = await Resources.LoadAsync<GameObject>(blockerPrefabPath) as GameObject;
}
if (blockerPrefabObj != null)
{
// 화면에서 가장 큰 그림판(Canvas)을 찾아서 그 위에 방패를 놓을 거예요.
Canvas mainCanvasForBlocker = instance.parentCanvas;
if (mainCanvasForBlocker != null)
{
// 방패를 복제해서(Instantiate) 그림판 위에 놓고, 가장 위로 오도록 순서를 조정해요.
currentBlockerInstance = UnityEngine.Object.Instantiate(blockerPrefabObj, mainCanvasForBlocker.transform);
int siblingIndex = instance.transform.GetSiblingIndex();
currentBlockerInstance.transform.SetSiblingIndex(Math.Max(0, siblingIndex - 1));
// 방패가 부드럽게 나타나도록 CanvasGroup 컴포넌트를 사용해요. 없으면 새로 추가!
CanvasGroup blockerCanvasGroup = currentBlockerInstance.GetComponent<CanvasGroup>();
if (blockerCanvasGroup == null) blockerCanvasGroup = currentBlockerInstance.AddComponent<CanvasGroup>();
blockerCanvasGroup.alpha = 0f;// 처음엔 완전히 투명하게
blockerCanvasGroup.DOFade(0.7f, 0.3f);// 0.3초 동안 서서히 나타나게 (투명도 70%)
}
else ULog.Error("[Modal] UIBlocker를 표시할 Canvas를 찾을 수 없습니다."); // 그림판을 못 찾으면 에러!
}
else ULog.Warning($"[Modal] UIBlocker 프리팹을 다음 경로에서 찾을 수 없습니다: {blockerPrefabPath}");
}
private static void HideBlock()
{
if (currentBlockerInstance != null)
{
var blockerCG = currentBlockerInstance.GetComponent<CanvasGroup>();
if (blockerCG != null)
{
blockerCG.DOFade(0f, 0.3f).OnComplete(() => {
UnityEngine.Object.Destroy(currentBlockerInstance); // 완전히 제거
}); // 부드럽게 사라지게
}
}
}
void Start()
{
Vector3 startPos = _item.transform.localPosition;
_dateItems.Clear();
_dateItems.Add(_item);
for (int i = 1; i < _totalDateNum; i++)
{
GameObject item = GameObject.Instantiate(_item) as GameObject;
item.name = "Item" + (i + 1).ToString();
item.transform.SetParent(_item.transform.parent);
item.transform.localScale = Vector3.one;
item.transform.localRotation = Quaternion.identity;
item.transform.localPosition = new Vector3((i % 7) * 36 + startPos.x, startPos.y - (i / 7) * 30, startPos.z);
_dateItems.Add(item);
}
//_dateTime = DateTime.Now;
CreateCalendar();
}
void CreateCalendar()
{
DateTime firstDay = _dateTime.AddDays(-(_dateTime.Day - 1));
int index = GetDays(firstDay.DayOfWeek);
int date = 0;
for (int i = 0; i < _totalDateNum; i++)
{
TextMeshProUGUI label = _dateItems[i].GetComponentInChildren<TextMeshProUGUI>();
_dateItems[i].SetActive(false);
DateTime day = firstDay.AddDays(date);
Color textColor = ColorUtil.FromHex("808080");
//토요일 색 변경
if (day.DayOfWeek == DayOfWeek.Saturday) //토요일
{
textColor = ColorUtil.FromHex("4A90E2");
}
else if (day.DayOfWeek == DayOfWeek.Sunday) //일요일
{
textColor = ColorUtil.FromHex("FF5E5E");
}
//오늘 날짜인지 확인해서 text 색 변경
if (day.Date == DateTime.Now.Date) textColor = Color.white;
label.color= textColor;
if (i >= index)
{
DateTime thatDay = firstDay.AddDays(date);
if (thatDay.Month == firstDay.Month)
{
_dateItems[i].SetActive(true);
label.text = (date + 1).ToString();
date++;
}
}
}
_yearNumText.text = _dateTime.Year.ToString();
_monthNumText.text = _dateTime.Month.ToString("D2");
}
int GetDays(DayOfWeek day)
{
switch (day)
{
case DayOfWeek.Monday: return 1;
case DayOfWeek.Tuesday: return 2;
case DayOfWeek.Wednesday: return 3;
case DayOfWeek.Thursday: return 4;
case DayOfWeek.Friday: return 5;
case DayOfWeek.Saturday: return 6;
case DayOfWeek.Sunday: return 0;
}
return 0;
}
public void YearPrev()
{
_dateTime = _dateTime.AddYears(-1);
CreateCalendar();
}
public void YearNext()
{
_dateTime = _dateTime.AddYears(1);
CreateCalendar();
}
public void MonthPrev()
{
_dateTime = _dateTime.AddMonths(-1);
CreateCalendar();
}
public void MonthNext()
{
_dateTime = _dateTime.AddMonths(1);
CreateCalendar();
}
//Item 클릭했을 경우 Text에 표시.
public void OnDateItemClick(string day)
{
string date = _yearNumText.text + "-" + _monthNumText.text + "-" + int.Parse(day).ToString("D2");
_onDateSelectedCallback?.Invoke(DateTime.Parse(date));
Hide();
}
}
}