#nullable enable using System; using UnityEngine.UIElements; namespace UVC.UIToolkit { /// /// 날짜시간 범위 속성 아이템 /// 시작, 종료 두 개의 DateTimePicker /// public class UTKDateTimeRangePropertyItem : UTKPropertyItemBase { #region Fields private UTKInputField? _startField; private UTKInputField? _endField; private UTKButton? _startPickerBtn; private UTKButton? _endPickerBtn; private UTKDatePicker? _currentPicker; private bool _isEditingStart; private string _dateTimeFormat = "yyyy-MM-dd HH:mm"; #endregion #region Properties public override UTKPropertyType PropertyType => UTKPropertyType.DateTimeRange; public string DateTimeFormat { get => _dateTimeFormat; set { _dateTimeFormat = value ?? "yyyy-MM-dd HH:mm"; RefreshUI(); } } #endregion #region Constructor public UTKDateTimeRangePropertyItem(string id, string name, UTKDateTimeRange initialValue = default, bool isReadOnly = false) : base(id, name, initialValue.Start == default ? new UTKDateTimeRange(DateTime.Now, DateTime.Now) : initialValue) { base._isReadOnly = isReadOnly; } public UTKDateTimeRangePropertyItem(string id, string name, DateTime start, DateTime end, bool isReadOnly = false) : base(id, name, new UTKDateTimeRange(start, end)) { base._isReadOnly = isReadOnly; } #endregion #region Override Methods public override VisualElement CreateUI() { var container = CreateUIFromUxml("UTKDateTimeRangePropertyItem"); if (container == null) { return CreateUIFallback(); } _startField = container.Q("start-field"); _endField = container.Q("end-field"); _startPickerBtn = container.Q("start-picker-btn"); _endPickerBtn = container.Q("end-picker-btn"); if (_startField != null) { _startField.Value = Value.Start.ToString(_dateTimeFormat); _startField.isReadOnly = IsReadOnly; } if (_endField != null) { _endField.Value = Value.End.ToString(_dateTimeFormat); _endField.isReadOnly = IsReadOnly; } if (_startPickerBtn != null) { _startPickerBtn.IsEnabled = !IsReadOnly; } if (_endPickerBtn != null) { _endPickerBtn.IsEnabled = !IsReadOnly; } return container; } private VisualElement CreateUIFallback() { var container = CreateContainer(); var label = CreateNameLabel(); container.Add(label); var valueContainer = new VisualElement(); valueContainer.AddToClassList("utk-property-item__value"); valueContainer.style.flexDirection = FlexDirection.Row; // Start field _startField = new UTKInputField(); _startField.name = "start-field"; _startField.Value = Value.Start.ToString(_dateTimeFormat); _startField.style.flexGrow = 1; _startField.isReadOnly = IsReadOnly; valueContainer.Add(_startField); _startPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary); _startPickerBtn.name = "start-picker-btn"; _startPickerBtn.IsEnabled = !IsReadOnly; _startPickerBtn.AddToClassList("utk-property-item__picker-btn"); valueContainer.Add(_startPickerBtn); var separator = new UTKLabel("~", UTKLabel.LabelSize.Body2); separator.AddToClassList("utk-property-item__range-separator"); valueContainer.Add(separator); // End field _endField = new UTKInputField(); _endField.name = "end-field"; _endField.Value = Value.End.ToString(_dateTimeFormat); _endField.style.flexGrow = 1; _endField.isReadOnly = IsReadOnly; valueContainer.Add(_endField); _endPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary); _endPickerBtn.name = "end-picker-btn"; _endPickerBtn.IsEnabled = !IsReadOnly; _endPickerBtn.AddToClassList("utk-property-item__picker-btn"); valueContainer.Add(_endPickerBtn); container.Add(valueContainer); return container; } public override void BindUI(VisualElement element) { base.BindUI(element); _startField = element.Q("start-field"); _endField = element.Q("end-field"); _startPickerBtn = element.Q("start-picker-btn"); _endPickerBtn = element.Q("end-picker-btn"); if (_startField != null) { _startField.Value = Value.Start.ToString(_dateTimeFormat); _startField.isReadOnly = IsReadOnly; _startField.OnValueChanged += OnStartTextChanged; } if (_endField != null) { _endField.Value = Value.End.ToString(_dateTimeFormat); _endField.isReadOnly = IsReadOnly; _endField.OnValueChanged += OnEndTextChanged; } if (_startPickerBtn != null) { _startPickerBtn.IsEnabled = !IsReadOnly; _startPickerBtn.OnClicked += OnStartPickerClicked; } if (_endPickerBtn != null) { _endPickerBtn.IsEnabled = !IsReadOnly; _endPickerBtn.OnClicked += OnEndPickerClicked; } } public override void UnbindUI(VisualElement element) { if (_startField != null) { _startField.OnValueChanged -= OnStartTextChanged; _startField = null; } if (_endField != null) { _endField.OnValueChanged -= OnEndTextChanged; _endField = null; } if (_startPickerBtn != null) { _startPickerBtn.OnClicked -= OnStartPickerClicked; _startPickerBtn = null; } if (_endPickerBtn != null) { _endPickerBtn.OnClicked -= OnEndPickerClicked; _endPickerBtn = null; } ClosePicker(); base.UnbindUI(element); } public override void RefreshUI() { if (_startField != null) { var formatted = Value.Start.ToString(_dateTimeFormat); if (_startField.Value != formatted) { _startField.SetValue(formatted, false); } } if (_endField != null) { var formatted = Value.End.ToString(_dateTimeFormat); if (_endField.Value != formatted) { _endField.SetValue(formatted, false); } } } protected override void UpdateReadOnlyState() { base.UpdateReadOnlyState(); if (_startField != null) _startField.isReadOnly = IsReadOnly; if (_endField != null) _endField.isReadOnly = IsReadOnly; if (_startPickerBtn != null) _startPickerBtn.IsEnabled = !IsReadOnly; if (_endPickerBtn != null) _endPickerBtn.IsEnabled = !IsReadOnly; } #endregion #region Private Methods private void OnStartPickerClicked() { _isEditingStart = true; OpenPicker(Value.Start); } private void OnEndPickerClicked() { _isEditingStart = false; OpenPicker(Value.End); } private void OpenPicker(DateTime initialDateTime) { if (_currentPicker != null || _rootElement == null) return; var root = _rootElement; while (root.parent != null) root = root.parent; string title = _isEditingStart ? $"{Name} - 시작" : $"{Name} - 종료"; _currentPicker = UTKDatePicker.Show(root, initialDateTime, UTKDatePicker.PickerMode.DateAndTime, title); _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) { if (_isEditingStart) { Value = new UTKDateTimeRange(dateTime, Value.End); } else { Value = new UTKDateTimeRange(Value.Start, dateTime); } ClosePicker(); } private void OnPickerClosed() => _currentPicker = null; private void OnStartTextChanged(string newValue) { if (DateTime.TryParse(newValue, out DateTime dateTime)) { var range = new UTKDateTimeRange(dateTime, Value.End); DebounceValueChange(range, 500).Forget(); } } private void OnEndTextChanged(string newValue) { if (DateTime.TryParse(newValue, out DateTime dateTime)) { var range = new UTKDateTimeRange(Value.Start, dateTime); DebounceValueChange(range, 500).Forget(); } } #endregion #region IDisposable protected override void Dispose(bool disposing) { if (disposing) ClosePicker(); base.Dispose(disposing); } #endregion } }