#nullable enable using System; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.UIElements; using UVC.Locale; using UVC.UIToolkit; namespace UVC.Sample.UIToolkit { /// /// UTKDatePicker 샘플 코드 /// 버튼 클릭으로 날짜 피커를 열고 선택된 날짜를 표시 /// public class UTKDatePickerSample : MonoBehaviour { [SerializeField] private UIDocument? _uiDocument; [SerializeField] [Tooltip("시작 시 적용할 테마")] private UTKTheme initialTheme = UTKTheme.Dark; private UTKToggle _themeToggle; private VisualElement? _root; private Label? _dateLabel; private Label? _dateTimeLabel; private Label? _dateRangeLabel; private DateTime _selectedDate = DateTime.Today; private DateTime _selectedDateTime = DateTime.Now; private DateTime _rangeStartDate = DateTime.Today; private DateTime _rangeEndDate = DateTime.Today.AddDays(7); private UTKDatePicker? _currentPicker; private void Start() { // UIDocument 참조 확인 var doc = GetComponent(); if (doc == null) { Debug.LogError("UIDocument가 할당되지 않았습니다."); return; } _uiDocument = doc; var toggle = _uiDocument.rootVisualElement.Q("toggle"); if (toggle == null) { Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다."); return; } _themeToggle = toggle; UTKThemeManager.Instance.RegisterRoot(_uiDocument.rootVisualElement); UTKThemeManager.Instance.SetTheme(initialTheme); _themeToggle.OnValueChanged += (isOn) => { UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light); }; bool success = LocalizationManager.Instance.LoadDefaultLocalizationData("ko", "locale.json"); Debug.Log($"LocalizationManager: LoadDefaultLocalizationData success: {success}"); _root = _uiDocument.rootVisualElement; CreateSampleUI(); } private void CreateSampleUI() { if (_root == null) return; // 메인 컨테이너 var container = new VisualElement(); container.style.position = Position.Absolute; container.style.left = 20; container.style.top = 20; container.style.backgroundColor = new Color(0.2f, 0.2f, 0.2f); container.style.borderTopLeftRadius = 8; container.style.borderTopRightRadius = 8; container.style.borderBottomLeftRadius = 8; container.style.borderBottomRightRadius = 8; container.style.paddingTop = 15; container.style.paddingBottom = 15; container.style.paddingLeft = 15; container.style.paddingRight = 15; container.style.width = 320; // 타이틀 var title = new Label("UTKDatePicker Sample"); title.style.fontSize = 16; title.style.unityFontStyleAndWeight = FontStyle.Bold; title.style.color = Color.white; title.style.marginBottom = 15; container.Add(title); // Date Only 섹션 var dateSection = CreateSection("Date Only Mode", "Open Date Picker", OpenDatePicker); _dateLabel = dateSection.label; _dateLabel.text = FormatDate(_selectedDate); container.Add(dateSection.container); // DateTime 섹션 var dateTimeSection = CreateSection("Date & Time Mode", "Open DateTime Picker", OpenDateTimePicker); _dateTimeLabel = dateTimeSection.label; _dateTimeLabel.text = FormatDateTime(_selectedDateTime); container.Add(dateTimeSection.container); // Async 섹션 var asyncSection = new VisualElement(); asyncSection.style.marginBottom = 15; var asyncLabel = new Label("Async/Await Mode"); asyncLabel.style.color = new Color(0.8f, 0.8f, 0.8f); asyncLabel.style.fontSize = 12; asyncLabel.style.marginBottom = 5; asyncSection.Add(asyncLabel); var asyncBtn = new Button(() => OpenDatePickerAsync().Forget()) { text = "Open Date Picker (Async)" }; asyncBtn.style.height = 32; asyncSection.Add(asyncBtn); container.Add(asyncSection); // Date Range 섹션 var rangeSection = CreateSection("Date Range Mode", "Open Range Picker", OpenDateRangePicker); _dateRangeLabel = rangeSection.label; _dateRangeLabel.text = FormatDateRange(_rangeStartDate, _rangeEndDate); container.Add(rangeSection.container); // Date Range Async 섹션 var rangeAsyncSection = new VisualElement(); rangeAsyncSection.style.marginBottom = 15; var rangeAsyncLabel = new Label("Date Range Async Mode"); rangeAsyncLabel.style.color = new Color(0.8f, 0.8f, 0.8f); rangeAsyncLabel.style.fontSize = 12; rangeAsyncLabel.style.marginBottom = 5; rangeAsyncSection.Add(rangeAsyncLabel); var rangeAsyncBtn = new Button(() => OpenDateRangePickerAsync().Forget()) { text = "Open Range Picker (Async)" }; rangeAsyncBtn.style.height = 32; rangeAsyncSection.Add(rangeAsyncBtn); container.Add(rangeAsyncSection); // 구분선 var separator = new VisualElement(); separator.style.height = 1; separator.style.backgroundColor = new Color(0.4f, 0.4f, 0.4f); separator.style.marginTop = 15; separator.style.marginBottom = 15; container.Add(separator); // 프리셋 날짜 버튼들 var presetLabel = new Label("Quick Select:"); presetLabel.style.color = Color.white; presetLabel.style.marginBottom = 10; container.Add(presetLabel); var presetRow = new VisualElement(); presetRow.style.flexDirection = FlexDirection.Row; presetRow.style.flexWrap = Wrap.Wrap; AddPresetButton(presetRow, "Today", DateTime.Today); AddPresetButton(presetRow, "Tomorrow", DateTime.Today.AddDays(1)); AddPresetButton(presetRow, "Next Week", DateTime.Today.AddDays(7)); AddPresetButton(presetRow, "Next Month", DateTime.Today.AddMonths(1)); container.Add(presetRow); _root.Add(container); } private (VisualElement container, Label label) CreateSection(string sectionTitle, string buttonText, Action onClick) { var section = new VisualElement(); section.style.marginBottom = 15; var sectionLabel = new Label(sectionTitle); sectionLabel.style.color = new Color(0.8f, 0.8f, 0.8f); sectionLabel.style.fontSize = 12; sectionLabel.style.marginBottom = 5; section.Add(sectionLabel); var row = new VisualElement(); row.style.flexDirection = FlexDirection.Row; row.style.alignItems = Align.Center; var valueLabel = new Label(); valueLabel.style.color = Color.white; valueLabel.style.fontSize = 14; valueLabel.style.flexGrow = 1; valueLabel.style.paddingTop = 5; valueLabel.style.paddingBottom = 5; valueLabel.style.paddingLeft = 10; valueLabel.style.paddingRight = 10; valueLabel.style.backgroundColor = new Color(0.15f, 0.15f, 0.15f); valueLabel.style.borderTopLeftRadius = 4; valueLabel.style.borderTopRightRadius = 4; valueLabel.style.borderBottomLeftRadius = 4; valueLabel.style.borderBottomRightRadius = 4; row.Add(valueLabel); var button = new Button(onClick) { text = buttonText }; button.style.marginLeft = 10; button.style.height = 28; row.Add(button); section.Add(row); return (section, valueLabel); } private void AddPresetButton(VisualElement parent, string label, DateTime date) { var btn = new Button(() => SetDate(date)) { text = label }; btn.style.marginRight = 5; btn.style.marginBottom = 5; btn.style.height = 28; parent.Add(btn); } private void OpenDatePicker() { if (_root == null || _currentPicker != null) return; _currentPicker = UTKDatePicker.Show( _root, _selectedDate, UTKDatePicker.PickerMode.DateOnly, "Select Date" ); _currentPicker.OnDateSelected += OnDateSelected; _currentPicker.OnClosed += OnPickerClosed; } private void OpenDateTimePicker() { if (_root == null || _currentPicker != null) return; _currentPicker = UTKDatePicker.Show( _root, _selectedDateTime, UTKDatePicker.PickerMode.DateAndTime, "Select Date & Time" ); _currentPicker.OnDateSelected += OnDateTimeSelected; _currentPicker.OnClosed += OnPickerClosed; } private async UniTaskVoid OpenDatePickerAsync() { if (_root == null) return; // ShowAsync를 사용하여 날짜 선택 대기 // OK 클릭 시 선택된 날짜 반환, 취소/닫기 시 null 반환 DateTime? result = await UTKDatePicker.ShowAsync( _root, _selectedDate, UTKDatePicker.PickerMode.DateOnly, "Select Date (Async)" ); if (result.HasValue) { _selectedDate = result.Value; if (_dateLabel != null) { _dateLabel.text = FormatDate(result.Value); } Debug.Log($"[Async] Date Result: {FormatDate(result.Value)}"); } else { Debug.Log("[Async] Date selection cancelled"); } } private void OnDateSelected(DateTime date) { _selectedDate = date; if (_dateLabel != null) { _dateLabel.text = FormatDate(date); } Debug.Log($"Date Selected: {FormatDate(date)}"); } private void OnDateTimeSelected(DateTime dateTime) { _selectedDateTime = dateTime; if (_dateTimeLabel != null) { _dateTimeLabel.text = FormatDateTime(dateTime); } Debug.Log($"DateTime Selected: {FormatDateTime(dateTime)}"); } private void OnPickerClosed() { if (_currentPicker != null) { _currentPicker.OnDateSelected -= OnDateSelected; _currentPicker.OnDateSelected -= OnDateTimeSelected; _currentPicker.OnClosed -= OnPickerClosed; _currentPicker = null; } } private void SetDate(DateTime date) { _selectedDate = date; _selectedDateTime = new DateTime(date.Year, date.Month, date.Day, _selectedDateTime.Hour, _selectedDateTime.Minute, 0); if (_dateLabel != null) { _dateLabel.text = FormatDate(_selectedDate); } if (_dateTimeLabel != null) { _dateTimeLabel.text = FormatDateTime(_selectedDateTime); } Debug.Log($"Preset Date Set: {FormatDate(date)}"); } private string FormatDate(DateTime date) { return date.ToString("yyyy-MM-dd"); } private string FormatDateTime(DateTime dateTime) { return dateTime.ToString("yyyy-MM-dd HH:mm"); } private string FormatDateRange(DateTime start, DateTime end) { return $"{start:yyyy-MM-dd} ~ {end:yyyy-MM-dd}"; } private void OpenDateRangePicker() { if (_root == null || _currentPicker != null) return; _currentPicker = UTKDatePicker.ShowRange( _root, _rangeStartDate, _rangeEndDate, false, "Select Date Range" ); _currentPicker.OnDateRangeSelected += OnDateRangeSelected; _currentPicker.OnClosed += OnPickerClosed; } private async UniTaskVoid OpenDateRangePickerAsync() { if (_root == null) return; // ShowRangeAsync를 사용하여 날짜 범위 선택 대기 // OK 클릭 시 선택된 범위 반환, 취소/닫기 시 null 반환 var result = await UTKDatePicker.ShowRangeAsync( _root, _rangeStartDate, _rangeEndDate, false, "Select Date Range (Async)" ); if (result.HasValue) { _rangeStartDate = result.Value.Start; _rangeEndDate = result.Value.End; if (_dateRangeLabel != null) { _dateRangeLabel.text = FormatDateRange(result.Value.Start, result.Value.End); } Debug.Log($"[Async] Range Result: {FormatDateRange(result.Value.Start, result.Value.End)}"); } else { Debug.Log("[Async] Date range selection cancelled"); } } private void OnDateRangeSelected(DateTime start, DateTime end) { _rangeStartDate = start; _rangeEndDate = end; if (_dateRangeLabel != null) { _dateRangeLabel.text = FormatDateRange(start, end); } Debug.Log($"Date Range Selected: {FormatDateRange(start, end)}"); } private void OnDestroy() { if (_currentPicker != null) { _currentPicker.OnDateSelected -= OnDateSelected; _currentPicker.OnDateSelected -= OnDateTimeSelected; _currentPicker.OnDateRangeSelected -= OnDateRangeSelected; _currentPicker.OnClosed -= OnPickerClosed; _currentPicker.Dispose(); _currentPicker = null; } } } }