#nullable enable using System; using UnityEngine; using UnityEngine.UIElements; namespace UVC.UIToolkit { /// /// DateTime 속성 View 클래스입니다. /// UTKInputField + UTKDatePicker (DateAndTime 모드)를 사용하여 날짜시간을 표시/편집합니다. /// [UxmlElement] public partial class UTKDateTimePropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView { #region Fields private UTKInputField? _dateTimeField; private UTKButton? _pickerButton; private UTKDatePicker? _currentPicker; private DateTime _value = DateTime.Now; private string _dateTimeFormat = "yyyy-MM-dd HH:mm"; private IUTKPropertyItem? _boundData; #endregion #region Properties protected override string ViewTypeName => "UTKDateTimePropertyItemView"; /// 현재 값 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("datetime-format")] public string DateTimeFormat { get => _dateTimeFormat; set { _dateTimeFormat = value ?? "yyyy-MM-dd HH:mm"; UpdateValueUI(); } } #endregion #region Events public event Action? OnValueChanged; #endregion #region Constructor public UTKDateTimePropertyItemView() : base() { InitializeUI(); } public UTKDateTimePropertyItemView(string label, DateTime value = default) : base() { _value = value == default ? DateTime.Now : value; Label = label; InitializeUI(); } #endregion #region Initialization private void InitializeUI() { AddToClassList("utk-property-item-view"); AddToClassList("utk-property-item-view--datetime"); if (!CreateUIFromUxml()) { CreateUIFallback(); } // UXML에서 요소 가져오기 QueryUIElements(); // 이벤트 등록 RegisterEvents(); UpdateValueUI(); UpdateReadOnlyState(); } private void QueryUIElements() { _dateTimeField = this.Q("datetime-field"); _pickerButton = this.Q("picker-btn"); // Fallback: UXML에서 못 찾으면 생성 if (_valueContainer != null) { if (_dateTimeField == null) { _dateTimeField = new UTKInputField { name = "datetime-field" }; _dateTimeField.style.flexGrow = 1; _dateTimeField.AddToClassList("utk-property-item-view__field"); _valueContainer.Add(_dateTimeField); } if (_pickerButton == null) { _pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary) { name = "picker-btn" }; _pickerButton.AddToClassList("utk-property-item-view__picker-btn"); _valueContainer.Add(_pickerButton); } } // 초기 값 설정 if (_dateTimeField != null) { _dateTimeField.SetValue(_value.ToString(_dateTimeFormat), false); _dateTimeField.isReadOnly = true;//IsReadOnly와 상관 없이 편집 불가 } if (_pickerButton != null) { _pickerButton.IsEnabled = !IsReadOnly; } } private void RegisterEvents() { if (_dateTimeField != null) { _dateTimeField.OnValueChanged += OnDateTimeTextChanged; } if (_pickerButton != null) { _pickerButton.OnClicked += OnPickerButtonClicked; } } private void UnregisterEvents() { if (_dateTimeField != null) { _dateTimeField.OnValueChanged -= OnDateTimeTextChanged; } 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 (_dateTimeField != null) _dateTimeField.isReadOnly = isReadOnly; if (_pickerButton != null) _pickerButton.style.display = isReadOnly ? DisplayStyle.None : DisplayStyle.Flex; } #endregion #region Event Handling private void OnPickerButtonClicked() { OpenPicker(); } private void OnDateTimeTextChanged(string newValue) { if (DateTime.TryParse(newValue, out DateTime dateTime)) { if (_value != dateTime) { _value = dateTime; OnValueChanged?.Invoke(_value); if (_boundData != null && _boundData.Value != _value) { _boundData.Value = _value; } } } } private void OpenPicker() { if (_currentPicker != null) return; var root = this as VisualElement; while (root.parent != null) { root = root.parent; } _currentPicker = UTKDatePicker.Show(root, _value, UTKDatePicker.PickerMode.DateAndTime, 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 dateTime) { Value = dateTime; ClosePicker(); } private void OnPickerClosed() { _currentPicker = null; } #endregion #region Value Update private void UpdateValueUI() { if (_dateTimeField != null) { var formatted = _value.ToString(_dateTimeFormat); if (_dateTimeField.Value != formatted) { _dateTimeField.SetValue(formatted, false); } } } #endregion #region Data Binding public void Bind(IUTKPropertyItem data) { if (data is IUTKPropertyItem dateTimeData) { Bind(dateTimeData); } else { Debug.LogWarning($"[UTKDateTimePropertyItemView] Cannot bind to non-DateTime data: {data.GetType().Name}"); } } public void Bind(IUTKPropertyItem data) { Unbind(); _boundData = data; Label = data.Name; _value = data.Value; IsReadOnly = data.IsReadOnly; IsVisible = data.IsVisible; TooltipText = data.Tooltip; if (data is UTKDateTimePropertyItem dateTimeItem) { _dateTimeFormat = dateTimeItem.DateTimeFormat; } data.OnTypedValueChanged += OnDataValueChanged; UpdateValueUI(); UpdateReadOnlyState(); } public void Unbind() { if (_boundData != null) { _boundData.OnTypedValueChanged -= OnDataValueChanged; _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; _dateTimeField = null; _pickerButton = null; } base.Dispose(disposing); } #endregion } }