Files
XRLib/Assets/Scripts/UVC/UIToolkit/Property/Views/UTKDropdownPropertyItemView.cs

389 lines
12 KiB
C#
Raw Normal View History

2026-02-04 20:31:52 +09:00
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit
{
/// <summary>
/// Dropdown 속성 View 클래스입니다.
/// UTKDropdown을 사용하여 문자열 목록에서 선택합니다.
/// ReadOnly 상태에서는 UTKInputField로 표시됩니다.
2026-02-04 20:31:52 +09:00
/// </summary>
[UxmlElement]
public partial class UTKDropdownPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<string>
{
#region Fields
private UTKDropdown? _dropdown;
private UTKInputField? _readOnlyField;
2026-02-04 20:31:52 +09:00
private string _value = string.Empty;
private List<string> _choices = new();
private IUTKPropertyItem<string>? _boundData;
#endregion
#region Properties
protected override string ViewTypeName => "UTKDropdownPropertyItemView";
/// <summary>현재 값</summary>
public string Value
{
get => _value;
set
{
if (_value != value)
{
_value = value ?? string.Empty;
UpdateValueUI();
OnValueChanged?.Invoke(_value);
if (_boundData != null && _boundData.Value != _value)
{
_boundData.Value = _value;
}
}
}
}
/// <summary>선택 가능한 항목 목록</summary>
public List<string> Choices
{
get => _choices;
set
{
_choices = value ?? new List<string>();
if (_dropdown != null)
{
_dropdown.SetOptions(_choices);
}
}
}
#endregion
#region Events
public event Action<string>? OnValueChanged;
#endregion
#region Constructor
public UTKDropdownPropertyItemView() : base()
{
InitializeUI();
}
public UTKDropdownPropertyItemView(UTKDropdownPropertyItem data) : base()
{
_choices = data.Choices;
_value = data.Value ?? (_choices.Count > 0 ? _choices[0] : string.Empty);
Label = data.Name;
_isReadOnly = data.IsReadOnly;
InitializeUI();
Bind(data);
}
public UTKDropdownPropertyItemView(string label, List<string> choices, string initialValue = "") : base()
{
_choices = choices ?? new List<string>();
_value = initialValue ?? (_choices.Count > 0 ? _choices[0] : string.Empty);
Label = label;
InitializeUI();
}
public UTKDropdownPropertyItemView(string label, IEnumerable<string> choices, int selectedIndex = 0) : base()
{
_choices = choices?.ToList() ?? new List<string>();
if (_choices.Count > 0 && selectedIndex >= 0 && selectedIndex < _choices.Count)
{
_value = _choices[selectedIndex];
}
Label = label;
InitializeUI();
}
#endregion
#region Initialization
private void InitializeUI()
{
AddToClassList("utk-property-item-view");
AddToClassList("utk-property-item-view--dropdown");
if (!CreateUIFromUxml())
{
CreateUIFallback();
}
// UXML에서 요소 가져오기
QueryUIElements();
// 이벤트 등록
RegisterEvents();
UpdateValueUI();
UpdateReadOnlyState();
}
private void QueryUIElements()
{
if (_valueContainer == null) return;
2026-02-04 20:31:52 +09:00
if (IsReadOnly)
2026-02-04 20:31:52 +09:00
{
// ReadOnly: UTKInputField 사용
_readOnlyField = this.Q<UTKInputField>("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;
_readOnlyField.isReadOnly = true;
2026-02-04 20:31:52 +09:00
// Dropdown 숨기기
if (_dropdown != null)
{
_dropdown.style.display = DisplayStyle.None;
}
}
else
2026-02-04 20:31:52 +09:00
{
// Editable: UTKDropdown 사용
_dropdown = this.Q<UTKDropdown>("dropdown-field");
if (_dropdown == null)
{
_dropdown = new UTKDropdown { name = "dropdown-field" };
_dropdown.AddToClassList("utk-property-item-view__dropdown");
_valueContainer.Add(_dropdown);
}
2026-02-04 20:31:52 +09:00
_dropdown.SetOptions(_choices);
int selectedIndex = _choices.IndexOf(_value);
_dropdown.SelectedIndex = Math.Max(0, selectedIndex);
_dropdown.IsEnabled = true;
_dropdown.style.display = DisplayStyle.Flex;
// ReadOnly 필드 숨기기
if (_readOnlyField != null)
{
_readOnlyField.style.display = DisplayStyle.None;
}
2026-02-04 20:31:52 +09:00
}
}
private void RegisterEvents()
{
if (_dropdown != null)
{
_dropdown.OnSelectionChanged += OnDropdownChanged;
}
}
private void UnregisterEvents()
{
if (_dropdown != null)
{
_dropdown.OnSelectionChanged -= OnDropdownChanged;
}
}
#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)
2026-02-04 20:31:52 +09:00
{
// 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;
_readOnlyField.isReadOnly = true;
_readOnlyField.style.display = DisplayStyle.Flex;
if (_dropdown != null)
{
_dropdown.style.display = DisplayStyle.None;
}
}
else
{
// InputField → Dropdown 전환
if (_dropdown == null)
{
_dropdown = new UTKDropdown { name = "dropdown-field" };
_dropdown.AddToClassList("utk-property-item-view__dropdown");
_valueContainer.Add(_dropdown);
_dropdown.SetOptions(_choices);
RegisterEvents();
}
int selectedIndex = _choices.IndexOf(_value);
_dropdown.SelectedIndex = Math.Max(0, selectedIndex);
_dropdown.IsEnabled = true;
_dropdown.style.display = DisplayStyle.Flex;
if (_readOnlyField != null)
{
_readOnlyField.style.display = DisplayStyle.None;
}
2026-02-04 20:31:52 +09:00
}
}
#endregion
#region Event Handling
private void OnDropdownChanged(int index, string newValue)
{
if (_value != newValue)
{
_value = newValue;
OnValueChanged?.Invoke(newValue);
if (_boundData != null && _boundData.Value != newValue)
{
_boundData.Value = newValue;
}
}
}
#endregion
#region Value Update
private void UpdateValueUI()
{
if (IsReadOnly && _readOnlyField != null)
{
_readOnlyField.Value = _value;
}
else if (_dropdown != null && _dropdown.SelectedValue != _value)
2026-02-04 20:31:52 +09:00
{
_dropdown.SetSelectedValue(_value, notify: false);
}
}
#endregion
#region Public Methods
/// <summary>선택 항목 추가</summary>
public void AddChoice(string choice)
{
if (!_choices.Contains(choice))
{
_choices.Add(choice);
if (_dropdown != null)
{
_dropdown.AddOption(choice);
}
}
}
/// <summary>선택 항목 제거</summary>
public bool RemoveChoice(string choice)
{
bool removed = _choices.Remove(choice);
if (removed && _dropdown != null)
{
_dropdown.SetOptions(_choices);
if (_value == choice && _choices.Count > 0)
{
Value = _choices[0];
}
}
return removed;
}
#endregion
#region Data Binding
public void Bind(IUTKPropertyItem data)
{
if (data is IUTKPropertyItem<string> stringData)
{
Bind(stringData);
}
else
{
Debug.LogWarning($"[UTKDropdownPropertyItemView] Cannot bind to non-string data: {data.GetType().Name}");
}
}
public void Bind(IUTKPropertyItem<string> data)
{
Unbind();
_boundData = data;
Label = data.Name;
_value = data.Value ?? string.Empty;
IsReadOnly = data.IsReadOnly;
IsVisible = data.IsVisible;
TooltipText = data.Tooltip;
if (data is UTKDropdownPropertyItem dropdownItem)
{
_choices = dropdownItem.Choices;
if (_dropdown != null)
{
_dropdown.SetOptions(_choices);
}
}
data.OnTypedValueChanged += OnDataValueChanged;
UpdateValueUI();
UpdateReadOnlyState();
}
public void Unbind()
{
if (_boundData != null)
{
_boundData.OnTypedValueChanged -= OnDataValueChanged;
_boundData = null;
}
}
private void OnDataValueChanged(IUTKPropertyItem<string> item, string oldValue, string newValue)
{
if (_value != newValue)
{
_value = newValue ?? string.Empty;
UpdateValueUI();
}
}
#endregion
#region Dispose
protected override void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
UnregisterEvents();
Unbind();
OnValueChanged = null;
_dropdown = null;
_readOnlyField = null;
2026-02-04 20:31:52 +09:00
}
base.Dispose(disposing);
}
#endregion
}
}