#nullable enable using System; using UnityEngine; using UnityEngine.UIElements; namespace UVC.UIToolkit { /// /// Enum 속성 View 클래스입니다. /// UTKEnumDropDown을 사용하여 열거형 값을 표시/편집합니다. /// ReadOnly 상태에서는 UTKInputField로 표시됩니다. /// [UxmlElement] public partial class UTKEnumPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView { #region Fields private UTKEnumDropDown? _enumDropdown; private UTKInputField? _readOnlyField; private Enum? _value; private Type? _enumType; private IUTKPropertyItem? _boundData; #endregion #region Properties protected override string ViewTypeName => "UTKEnumPropertyItemView"; /// 현재 값 public Enum Value { get => _value!; set { if (_value != value) { _value = value; _enumType = value?.GetType(); UpdateValueUI(); OnValueChanged?.Invoke(value!); if (_boundData != null && _boundData.Value != value) { _boundData.Value = value!; } } } } /// 열거형 타입 public Type? EnumType => _enumType; #endregion #region Events public event Action? OnValueChanged; #endregion #region Constructor public UTKEnumPropertyItemView() : base() { InitializeUI(); } public UTKEnumPropertyItemView(UTKEnumPropertyItem data) : base() { if (data == null) throw new ArgumentNullException(nameof(data)); _value = data.Value; _enumType = data.Value?.GetType(); Label = data.Name; _isReadOnly = data.IsReadOnly; InitializeUI(); Bind(data); } public UTKEnumPropertyItemView(string label, Enum initialValue) : base() { _value = initialValue ?? throw new ArgumentNullException(nameof(initialValue)); _enumType = initialValue.GetType(); Label = label; InitializeUI(); } #endregion #region Initialization private void InitializeUI() { AddToClassList("utk-property-item-view"); AddToClassList("utk-property-item-view--enum"); if (!CreateUIFromUxml()) { CreateUIFallback(); } // UXML에서 요소 가져오기 QueryUIElements(); // 이벤트 등록 RegisterEvents(); UpdateValueUI(); UpdateReadOnlyState(); } private void QueryUIElements() { if (_valueContainer == null) return; if (IsReadOnly) { // ReadOnly: UTKInputField 사용 _readOnlyField = this.Q("readonly-field"); if (_readOnlyField == null) { _readOnlyField = new UTKInputField { name = "readonly-field" }; _readOnlyField.AddToClassList("utk-property-item-view__readonly-field"); _valueContainer.Add(_readOnlyField); } _readOnlyField.Value = _value?.ToString() ?? ""; _readOnlyField.isReadOnly = true; // Dropdown 숨기기 if (_enumDropdown != null) { _enumDropdown.style.display = DisplayStyle.None; } } else { // Editable: UTKEnumDropDown 사용 _enumDropdown = this.Q("enum-dropdown"); if (_enumDropdown == null) { _enumDropdown = new UTKEnumDropDown { name = "enum-dropdown" }; _enumDropdown.AddToClassList("utk-property-item-view__dropdown"); _valueContainer.Add(_enumDropdown); } if (_value != null) { _enumDropdown.Init(_value); } _enumDropdown.IsEnabled = true; _enumDropdown.style.display = DisplayStyle.Flex; // ReadOnly 필드 숨기기 if (_readOnlyField != null) { _readOnlyField.style.display = DisplayStyle.None; } } } private void RegisterEvents() { if (_enumDropdown != null) { _enumDropdown.OnValueChanged += OnEnumDropdownChanged; } } private void UnregisterEvents() { if (_enumDropdown != null) { _enumDropdown.OnValueChanged -= OnEnumDropdownChanged; } } #endregion #region Override Methods protected override void CreateValueUI(VisualElement container) { // UXML/QueryUIElements 기반으로 생성하므로 여기서는 생성하지 않음 } public override void RefreshUI() { UpdateValueUI(); } protected override void OnReadOnlyStateChanged(bool isReadOnly) { if (_valueContainer == null) return; if (isReadOnly) { // Dropdown → InputField 전환 if (_readOnlyField == null) { _readOnlyField = new UTKInputField { name = "readonly-field" }; _readOnlyField.AddToClassList("utk-property-item-view__readonly-field"); _valueContainer.Add(_readOnlyField); } _readOnlyField.Value = _value?.ToString() ?? ""; _readOnlyField.isReadOnly = true; _readOnlyField.style.display = DisplayStyle.Flex; if (_enumDropdown != null) { _enumDropdown.style.display = DisplayStyle.None; } } else { // InputField → Dropdown 전환 if (_enumDropdown == null) { _enumDropdown = new UTKEnumDropDown { name = "enum-dropdown" }; _enumDropdown.AddToClassList("utk-property-item-view__dropdown"); _valueContainer.Add(_enumDropdown); if (_value != null) { _enumDropdown.Init(_value); } RegisterEvents(); } _enumDropdown.IsEnabled = true; _enumDropdown.style.display = DisplayStyle.Flex; if (_readOnlyField != null) { _readOnlyField.style.display = DisplayStyle.None; } } } #endregion #region Event Handling private void OnEnumDropdownChanged(Enum? newValue) { if (newValue != null && !Equals(_value, newValue)) { _value = newValue; OnValueChanged?.Invoke(newValue); if (_boundData != null && !Equals(_boundData.Value, newValue)) { _boundData.Value = newValue; } } } #endregion #region Value Update private void UpdateValueUI() { if (IsReadOnly && _readOnlyField != null) { _readOnlyField.Value = _value?.ToString() ?? ""; } else if (_enumDropdown != null && _value != null) { if (_enumDropdown.Value != _value) { _enumDropdown.Value = _value; } } } #endregion #region Data Binding public void Bind(IUTKPropertyItem data) { if (data is IUTKPropertyItem enumData) { Bind(enumData); } else { Debug.LogWarning($"[UTKEnumPropertyItemView] Cannot bind to non-Enum data: {data.GetType().Name}"); } } public void Bind(IUTKPropertyItem data) { Unbind(); _boundData = data; Label = data.Name; _value = data.Value; _enumType = data.Value?.GetType(); IsReadOnly = data.IsReadOnly; IsVisible = data.IsVisible; TooltipText = data.Tooltip; if (_enumDropdown != null && _value != null) { _enumDropdown.Init(_value); } data.OnTypedValueChanged += OnDataValueChanged; UpdateValueUI(); UpdateReadOnlyState(); } public void Unbind() { if (_boundData != null) { _boundData.OnTypedValueChanged -= OnDataValueChanged; _boundData = null; } } private void OnDataValueChanged(IUTKPropertyItem item, Enum oldValue, Enum newValue) { if (!Equals(_value, newValue)) { _value = newValue; UpdateValueUI(); } } #endregion #region Dispose protected override void Dispose(bool disposing) { if (_disposed) return; if (disposing) { UnregisterEvents(); Unbind(); OnValueChanged = null; _enumDropdown = null; _readOnlyField = null; } base.Dispose(disposing); } #endregion } }