#nullable enable using System; using UnityEngine; using UnityEngine.UIElements; namespace UVC.UIToolkit { /// /// Date 속성 View 클래스입니다. /// UTKInputField + UTKDatePicker를 사용하여 날짜를 표시/편집합니다. /// [UxmlElement] public partial class UTKDatePropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView { #region Fields private UTKInputField? _dateField; private UTKButton? _pickerButton; private UTKDatePicker? _currentPicker; private DateTime _value = DateTime.Today; private string _dateFormat = "yyyy-MM-dd"; private IUTKPropertyItem? _boundData; #endregion #region Properties protected override string ViewTypeName => "UTKDatePropertyItemView"; /// 현재 값 public DateTime Value { get => _value; set { if (_value != value) { _value = value; UpdateValueUI(); OnValueChanged?.Invoke(value); if (_boundData != null && _boundData.Value != value) { _boundData.Value = value; } } } } /// 날짜 표시 형식 [UxmlAttribute("date-format")] public string DateFormat { get => _dateFormat; set { _dateFormat = value ?? "yyyy-MM-dd"; UpdateValueUI(); } } #endregion #region Events public event Action? OnValueChanged; #endregion #region Constructor public UTKDatePropertyItemView() : base() { InitializeUI(); } public UTKDatePropertyItemView(string label, DateTime value = default) : base() { _value = value == default ? DateTime.Today : value; Label = label; InitializeUI(); } #endregion #region Initialization private void InitializeUI() { AddToClassList("utk-property-item-view"); AddToClassList("utk-property-item-view--date"); if (!CreateUIFromUxml()) { CreateUIFallback(); } // UXML에서 요소 가져오기 QueryUIElements(); // 이벤트 등록 RegisterEvents(); UpdateValueUI(); UpdateReadOnlyState(); } private void QueryUIElements() { _dateField = this.Q("date-field"); _pickerButton = this.Q("picker-btn"); // Fallback: UXML에서 못 찾으면 생성 if (_valueContainer != null) { if (_dateField == null) { _dateField = new UTKInputField { name = "date-field" }; _dateField.style.flexGrow = 1; _dateField.AddToClassList("utk-property-item-view__field"); _valueContainer.Add(_dateField); } if (_pickerButton == null) { _pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary) { name = "picker-btn" }; _pickerButton.AddToClassList("utk-property-item-view__picker-btn"); _valueContainer.Add(_pickerButton); } } // 초기 값 설정 if (_dateField != null) { _dateField.SetValue(_value.ToString(_dateFormat), false); _dateField.isReadOnly = true;//IsReadOnly와 상관 없이 편집 불가 } if (_pickerButton != null) { _pickerButton.IsEnabled = !IsReadOnly; } } private void RegisterEvents() { if (_dateField != null) { _dateField.OnValueChanged += OnDateTextChanged; } if (_pickerButton != null) { _pickerButton.OnClicked += OnPickerButtonClicked; } } private void UnregisterEvents() { if (_dateField != null) { _dateField.OnValueChanged -= OnDateTextChanged; } if (_pickerButton != null) { _pickerButton.OnClicked -= OnPickerButtonClicked; } } #endregion #region Override Methods protected override void CreateValueUI(VisualElement container) { // UXML/QueryUIElements 기반으로 생성하므로 여기서는 생성하지 않음 } public override void RefreshUI() { UpdateValueUI(); } protected override void OnReadOnlyStateChanged(bool isReadOnly) { // if (_dateField != null) _dateField.isReadOnly = isReadOnly;// 항상 편집 불가 if (_pickerButton != null) _pickerButton.style.display = isReadOnly ? DisplayStyle.None : DisplayStyle.Flex; } #endregion #region Event Handling private void OnPickerButtonClicked() { OpenPicker(); } private void OnDateTextChanged(string newValue) { if (DateTime.TryParse(newValue, out DateTime date)) { if (_value.Date != date.Date) { _value = date.Date; OnValueChanged?.Invoke(_value); if (_boundData != null && _boundData.Value != _value) { _boundData.Value = _value; } } } } private void OpenPicker() { if (_currentPicker != null) return; _currentPicker = UTKDatePicker.Show(_value, UTKDatePicker.PickerMode.DateOnly, Label); _currentPicker.OnDateSelected += OnPickerDateSelected; _currentPicker.OnClosed += OnPickerClosed; } private void ClosePicker() { if (_currentPicker != null) { _currentPicker.OnDateSelected -= OnPickerDateSelected; _currentPicker.OnClosed -= OnPickerClosed; _currentPicker.Close(); _currentPicker = null; } } private void OnPickerDateSelected(DateTime date) { Value = date.Date; ClosePicker(); } private void OnPickerClosed() { _currentPicker = null; } #endregion #region Value Update private void UpdateValueUI() { if (_dateField != null) { var formatted = _value.ToString(_dateFormat); if (_dateField.Value != formatted) { _dateField.SetValue(formatted, false); } } } #endregion #region Data Binding public void Bind(IUTKPropertyItem data) { if (data is IUTKPropertyItem dateData) { Bind(dateData); } else { Debug.LogWarning($"[UTKDatePropertyItemView] Cannot bind to non-DateTime data: {data.GetType().Name}"); } } public void Bind(IUTKPropertyItem data) { Unbind(); _boundData = data; BindBase(data); Label = data.Name; _value = data.Value; IsVisible = data.IsVisible; TooltipText = data.Tooltip; ShowLabel = data.ShowLabel; if (data is UTKDatePropertyItem dateItem) { _dateFormat = dateItem.DateFormat; } data.OnTypedValueChanged += OnDataValueChanged; UpdateValueUI(); IsReadOnly = data.IsReadOnly; } public void Unbind() { if (_boundData != null) { _boundData.OnTypedValueChanged -= OnDataValueChanged; UnbindBase(); _boundData = null; } } private void OnDataValueChanged(IUTKPropertyItem item, DateTime oldValue, DateTime newValue) { if (_value != newValue) { _value = newValue; UpdateValueUI(); } } #endregion #region Dispose protected override void Dispose(bool disposing) { if (_disposed) return; if (disposing) { ClosePicker(); UnregisterEvents(); Unbind(); OnValueChanged = null; _dateField = null; _pickerButton = null; } base.Dispose(disposing); } #endregion } }