#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit
{
///
/// MultiSelectDropdown 속성 View 클래스입니다.
/// UTKMultiSelectDropdown을 사용하여 문자열 목록에서 다중 선택합니다.
/// ReadOnly 상태에서는 UTKInputField로 표시됩니다.
///
[UxmlElement]
public partial class UTKMultiSelectDropdownPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView>
{
#region Fields
private UTKMultiSelectDropdown? _multiSelectDropdown;
private UTKInputField? _readOnlyField;
private List _value = new();
private List _choices = new();
private IUTKPropertyItem>? _boundData;
#endregion
#region Properties
protected override string ViewTypeName => "UTKMultiSelectDropdownPropertyItemView";
/// 현재 값 (선택된 항목 목록)
public List Value
{
get => _value;
set
{
var newValue = value ?? new List();
// 리스트 내용 비교
if (!AreListsEqual(_value, newValue))
{
_value = new List(newValue);
UpdateValueUI();
OnValueChanged?.Invoke(_value);
if (_boundData != null && !AreListsEqual(_boundData.Value, _value))
{
_boundData.Value = new List(_value);
}
}
}
}
/// 선택 가능한 항목 목록
public List Choices
{
get => _choices;
set
{
_choices = value ?? new List();
if (_multiSelectDropdown != null)
{
_multiSelectDropdown.SetOptions(_choices);
}
}
}
#endregion
#region Events
public event Action>? OnValueChanged;
#endregion
#region Constructor
public UTKMultiSelectDropdownPropertyItemView() : base()
{
InitializeUI();
}
public UTKMultiSelectDropdownPropertyItemView(UTKMultiSelectDropdownPropertyItem data) : base()
{
_choices = data.Choices;
_value = data.Value != null ? new List(data.Value) : new List();
Label = data.Name;
_isReadOnly = data.IsReadOnly;
InitializeUI();
Bind(data);
}
public UTKMultiSelectDropdownPropertyItemView(
string label,
List choices,
List? initialValues = null) : base()
{
_choices = choices ?? new List();
_value = initialValues != null ? new List(initialValues) : new List();
Label = label;
InitializeUI();
}
public UTKMultiSelectDropdownPropertyItemView(
string label,
IEnumerable choices,
IEnumerable? selectedIndices = null) : base()
{
_choices = choices?.ToList() ?? new List();
if (selectedIndices != null)
{
_value = selectedIndices
.Where(i => i >= 0 && i < _choices.Count)
.Select(i => _choices[i])
.ToList();
}
Label = label;
InitializeUI();
}
#endregion
#region Initialization
private void InitializeUI()
{
AddToClassList("utk-property-item-view");
AddToClassList("utk-property-item-view--multiselect-dropdown");
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 = string.Join(", ", _value);
_readOnlyField.isReadOnly = true;
// Dropdown 숨기기
if (_multiSelectDropdown != null)
{
_multiSelectDropdown.style.display = DisplayStyle.None;
}
}
else
{
// Editable: UTKMultiSelectDropdown 사용
_multiSelectDropdown = this.Q("multiselect-dropdown-field");
if (_multiSelectDropdown == null)
{
_multiSelectDropdown = new UTKMultiSelectDropdown { name = "multiselect-dropdown-field" };
_multiSelectDropdown.AddToClassList("utk-property-item-view__multiselect-dropdown");
_valueContainer.Add(_multiSelectDropdown);
}
_multiSelectDropdown.SetOptions(_choices);
_multiSelectDropdown.SetSelectedValues(_value, notify: false);
_multiSelectDropdown.IsEnabled = true;
_multiSelectDropdown.style.display = DisplayStyle.Flex;
// ReadOnly 필드 숨기기
if (_readOnlyField != null)
{
_readOnlyField.style.display = DisplayStyle.None;
}
}
}
private void RegisterEvents()
{
if (_multiSelectDropdown != null)
{
_multiSelectDropdown.OnSelectionChanged += OnMultiSelectDropdownChanged;
}
}
private void UnregisterEvents()
{
if (_multiSelectDropdown != null)
{
_multiSelectDropdown.OnSelectionChanged -= OnMultiSelectDropdownChanged;
}
}
#endregion
#region Override Methods
protected override void CreateValueUI(VisualElement container)
{
// Fallback: UXML 로드 실패 시 코드로 생성
_multiSelectDropdown = new UTKMultiSelectDropdown { name = "multiselect-dropdown-field" };
_multiSelectDropdown.AddToClassList("utk-property-item-view__multiselect-dropdown");
container.Add(_multiSelectDropdown);
}
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 = string.Join(", ", _value);
_readOnlyField.isReadOnly = true;
_readOnlyField.style.display = DisplayStyle.Flex;
if (_multiSelectDropdown != null)
{
_multiSelectDropdown.style.display = DisplayStyle.None;
}
}
else
{
// InputField → Dropdown 전환
if (_multiSelectDropdown == null)
{
_multiSelectDropdown = new UTKMultiSelectDropdown { name = "multiselect-dropdown-field" };
_multiSelectDropdown.AddToClassList("utk-property-item-view__multiselect-dropdown");
_valueContainer.Add(_multiSelectDropdown);
_multiSelectDropdown.SetOptions(_choices);
RegisterEvents();
}
_multiSelectDropdown.SetSelectedValues(_value, notify: false);
_multiSelectDropdown.IsEnabled = true;
_multiSelectDropdown.style.display = DisplayStyle.Flex;
if (_readOnlyField != null)
{
_readOnlyField.style.display = DisplayStyle.None;
}
}
}
#endregion
#region Event Handling
private void OnMultiSelectDropdownChanged(List indices, List values)
{
if (!AreListsEqual(_value, values))
{
_value = new List(values);
OnValueChanged?.Invoke(_value);
if (_boundData != null && !AreListsEqual(_boundData.Value, _value))
{
_boundData.Value = new List(_value);
}
}
}
#endregion
#region Value Update
private void UpdateValueUI()
{
if (IsReadOnly && _readOnlyField != null)
{
_readOnlyField.Value = string.Join(", ", _value);
}
else if (_multiSelectDropdown != null && !AreListsEqual(_multiSelectDropdown.SelectedValues, _value))
{
_multiSelectDropdown.SetSelectedValues(_value, notify: false);
}
}
#endregion
#region Helper Methods
/// 두 리스트의 내용이 같은지 비교 (순서 무관)
private bool AreListsEqual(List? list1, List? list2)
{
if (list1 == null && list2 == null) return true;
if (list1 == null || list2 == null) return false;
if (list1.Count != list2.Count) return false;
var set1 = new HashSet(list1);
var set2 = new HashSet(list2);
return set1.SetEquals(set2);
}
#endregion
#region Public Methods
/// 선택 항목 추가
public void AddChoice(string choice)
{
if (!_choices.Contains(choice))
{
_choices.Add(choice);
if (_multiSelectDropdown != null)
{
_multiSelectDropdown.SetOptions(_choices);
}
}
}
/// 선택 항목 제거
public bool RemoveChoice(string choice)
{
bool removed = _choices.Remove(choice);
if (removed && _multiSelectDropdown != null)
{
_multiSelectDropdown.SetOptions(_choices);
if (_value.Contains(choice))
{
var newValue = _value.Where(v => v != choice).ToList();
Value = newValue;
}
}
return removed;
}
/// 인덱스로 선택 설정
public void SetSelectedIndices(List indices, bool notify = true)
{
if (_multiSelectDropdown != null)
{
_multiSelectDropdown.SetSelectedIndices(indices, notify);
}
}
/// 값으로 선택 설정
public void SetSelectedValues(List values, bool notify = true)
{
if (_multiSelectDropdown != null)
{
_multiSelectDropdown.SetSelectedValues(values, notify);
}
}
/// 모든 항목 선택
public void SelectAll()
{
_multiSelectDropdown?.SelectAll();
}
/// 모든 선택 해제
public void ClearSelection()
{
_multiSelectDropdown?.ClearSelection();
}
#endregion
#region Data Binding
public void Bind(IUTKPropertyItem data)
{
if (data is IUTKPropertyItem> listData)
{
Bind(listData);
}
else
{
Debug.LogWarning($"[UTKMultiSelectDropdownPropertyItemView] Cannot bind to non-List data: {data.GetType().Name}");
}
}
public void Bind(IUTKPropertyItem> data)
{
Unbind();
_boundData = data;
BindBase(data);
Label = data.Name;
_value = data.Value != null ? new List(data.Value) : new List();
IsVisible = data.IsVisible;
TooltipText = data.Tooltip;
ShowLabel = data.ShowLabel;
if (data is UTKMultiSelectDropdownPropertyItem multiSelectItem)
{
_choices = multiSelectItem.Choices;
if (_multiSelectDropdown != null)
{
_multiSelectDropdown.SetOptions(_choices);
}
}
data.OnTypedValueChanged += OnDataValueChanged;
UpdateValueUI();
IsReadOnly = data.IsReadOnly;
}
public void Unbind()
{
if (_boundData != null)
{
_boundData.OnTypedValueChanged -= OnDataValueChanged;
UnbindBase();
_boundData = null;
}
}
private void OnDataValueChanged(IUTKPropertyItem> item, List oldValue, List newValue)
{
if (!AreListsEqual(_value, newValue))
{
_value = newValue != null ? new List(newValue) : new List();
UpdateValueUI();
}
}
#endregion
#region Dispose
protected override void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
UnregisterEvents();
Unbind();
OnValueChanged = null;
_multiSelectDropdown = null;
_readOnlyField = null;
}
base.Dispose(disposing);
}
#endregion
}
}