Files
XRLib/Assets/Scripts/UVC/UIToolkit/Property/Items/UTKDateTimeRangePropertyItem.cs

273 lines
8.6 KiB
C#
Raw Normal View History

2026-01-08 20:15:57 +09:00
#nullable enable
using System;
using UnityEngine.UIElements;
using UVC.UIToolkit.Modal;
namespace UVC.UIToolkit.Property
{
/// <summary>
/// 날짜시간 범위 속성 아이템
/// 시작, 종료 두 개의 DateTimePicker
/// </summary>
public class UTKDateTimeRangePropertyItem : UTKPropertyItemBase<UTKDateTimeRange>
{
#region Fields
private TextField? _startField;
private TextField? _endField;
private Button? _startPickerBtn;
private Button? _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)
: base(id, name, initialValue.Start == default ? new UTKDateTimeRange(DateTime.Now, DateTime.Now) : initialValue)
{
}
public UTKDateTimeRangePropertyItem(string id, string name, DateTime start, DateTime end)
: base(id, name, new UTKDateTimeRange(start, end))
{
}
#endregion
#region Override Methods
public override VisualElement CreateUI()
{
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 TextField();
_startField.name = "start-field";
_startField.value = Value.Start.ToString(_dateTimeFormat);
_startField.style.flexGrow = 1;
valueContainer.Add(_startField);
_startPickerBtn = new Button { text = "..." };
_startPickerBtn.name = "start-picker-btn";
_startPickerBtn.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_startPickerBtn);
var separator = new Label("~");
separator.AddToClassList("utk-property-item__range-separator");
valueContainer.Add(separator);
// End field
_endField = new TextField();
_endField.name = "end-field";
_endField.value = Value.End.ToString(_dateTimeFormat);
_endField.style.flexGrow = 1;
valueContainer.Add(_endField);
_endPickerBtn = new Button { text = "..." };
_endPickerBtn.name = "end-picker-btn";
_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<TextField>("start-field");
_endField = element.Q<TextField>("end-field");
_startPickerBtn = element.Q<Button>("start-picker-btn");
_endPickerBtn = element.Q<Button>("end-picker-btn");
if (_startField != null)
{
_startField.value = Value.Start.ToString(_dateTimeFormat);
_startField.SetEnabled(!IsReadOnly);
_startField.RegisterValueChangedCallback(OnStartTextChanged);
}
if (_endField != null)
{
_endField.value = Value.End.ToString(_dateTimeFormat);
_endField.SetEnabled(!IsReadOnly);
_endField.RegisterValueChangedCallback(OnEndTextChanged);
}
if (_startPickerBtn != null)
{
_startPickerBtn.SetEnabled(!IsReadOnly);
_startPickerBtn.clicked += OnStartPickerClicked;
}
if (_endPickerBtn != null)
{
_endPickerBtn.SetEnabled(!IsReadOnly);
_endPickerBtn.clicked += OnEndPickerClicked;
}
}
public override void UnbindUI(VisualElement element)
{
if (_startField != null)
{
_startField.UnregisterValueChangedCallback(OnStartTextChanged);
_startField = null;
}
if (_endField != null)
{
_endField.UnregisterValueChangedCallback(OnEndTextChanged);
_endField = null;
}
if (_startPickerBtn != null)
{
_startPickerBtn.clicked -= OnStartPickerClicked;
_startPickerBtn = null;
}
if (_endPickerBtn != null)
{
_endPickerBtn.clicked -= 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.SetValueWithoutNotify(formatted);
}
}
if (_endField != null)
{
var formatted = Value.End.ToString(_dateTimeFormat);
if (_endField.value != formatted)
{
_endField.SetValueWithoutNotify(formatted);
}
}
}
protected override void UpdateReadOnlyState()
{
base.UpdateReadOnlyState();
_startField?.SetEnabled(!IsReadOnly);
_endField?.SetEnabled(!IsReadOnly);
_startPickerBtn?.SetEnabled(!IsReadOnly);
_endPickerBtn?.SetEnabled(!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.Dispose();
_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(ChangeEvent<string> evt)
{
if (DateTime.TryParse(evt.newValue, out DateTime dateTime))
{
var newValue = new UTKDateTimeRange(dateTime, Value.End);
DebounceValueChange(newValue, 500).Forget();
}
}
private void OnEndTextChanged(ChangeEvent<string> evt)
{
if (DateTime.TryParse(evt.newValue, out DateTime dateTime))
{
var newValue = new UTKDateTimeRange(Value.Start, dateTime);
DebounceValueChange(newValue, 500).Forget();
}
}
#endregion
#region IDisposable
protected override void Dispose(bool disposing)
{
if (disposing) ClosePicker();
base.Dispose(disposing);
}
#endregion
}
}