UTKPropertyItem 개선

This commit is contained in:
logonkhi
2026-02-04 20:31:52 +09:00
parent 8181eae4c6
commit c9af0d2d6f
202 changed files with 8337 additions and 3878 deletions

View File

@@ -0,0 +1,296 @@
#nullable enable
using System;
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit
{
/// <summary>
/// IntRange 속성 View 클래스입니다.
/// Min, Max 두 개의 UTKIntegerField를 사용하여 정수 범위를 표시/편집합니다.
/// </summary>
[UxmlElement]
public partial class UTKIntRangePropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<UTKIntRange>
{
#region Fields
private UTKIntegerField? _minField;
private UTKIntegerField? _maxField;
private UTKIntRange _value;
private IUTKPropertyItem<UTKIntRange>? _boundData;
#endregion
#region Properties
protected override string ViewTypeName => "UTKIntRangePropertyItemView";
/// <summary>현재 값</summary>
public UTKIntRange Value
{
get => _value;
set
{
if (!_value.Equals(value))
{
_value = value;
UpdateValueUI();
OnValueChanged?.Invoke(value);
if (_boundData != null && !_boundData.Value.Equals(value))
{
_boundData.Value = value;
}
}
}
}
#endregion
#region Events
public event Action<UTKIntRange>? OnValueChanged;
#endregion
#region Constructor
public UTKIntRangePropertyItemView() : base()
{
InitializeUI();
}
public UTKIntRangePropertyItemView(string label, UTKIntRange value = default) : base()
{
_value = value;
Label = label;
InitializeUI();
}
public UTKIntRangePropertyItemView(string label, int min, int max) : base()
{
_value = new UTKIntRange(min, max);
Label = label;
InitializeUI();
}
#endregion
#region Initialization
private void InitializeUI()
{
AddToClassList("utk-property-item-view");
AddToClassList("utk-property-item-view--int-range");
if (!CreateUIFromUxml())
{
CreateUIFallback();
}
// UXML에서 요소 가져오기
QueryUIElements();
// 이벤트 등록
RegisterEvents();
UpdateValueUI();
UpdateReadOnlyState();
}
private void QueryUIElements()
{
_minField = this.Q<UTKIntegerField>("min-field");
_maxField = this.Q<UTKIntegerField>("max-field");
// Fallback: UXML에서 못 찾으면 생성
if (_valueContainer != null)
{
_valueContainer.style.flexDirection = FlexDirection.Row;
if (_minField == null)
{
_minField = new UTKIntegerField { name = "min-field" };
_minField.style.flexGrow = 1;
_minField.AddToClassList("utk-property-item-view__field");
_valueContainer.Add(_minField);
var separator = new UTKLabel("~", UTKLabel.LabelSize.Body2);
separator.AddToClassList("utk-property-item-view__range-separator");
_valueContainer.Add(separator);
}
if (_maxField == null)
{
_maxField = new UTKIntegerField { name = "max-field" };
_maxField.style.flexGrow = 1;
_maxField.AddToClassList("utk-property-item-view__field");
_valueContainer.Add(_maxField);
}
}
// 초기 값 설정
if (_minField != null)
{
_minField.SetValueWithoutNotify(_value.Min);
_minField.isReadOnly = IsReadOnly;
}
if (_maxField != null)
{
_maxField.SetValueWithoutNotify(_value.Max);
_maxField.isReadOnly = IsReadOnly;
}
}
private void RegisterEvents()
{
if (_minField != null)
{
_minField.OnValueChanged += OnMinChanged;
}
if (_maxField != null)
{
_maxField.OnValueChanged += OnMaxChanged;
}
}
private void UnregisterEvents()
{
if (_minField != null)
{
_minField.OnValueChanged -= OnMinChanged;
}
if (_maxField != null)
{
_maxField.OnValueChanged -= OnMaxChanged;
}
}
#endregion
#region Override Methods
protected override void CreateValueUI(VisualElement container)
{
// UXML/QueryUIElements 기반으로 생성하므로 여기서는 생성하지 않음
}
public override void RefreshUI()
{
UpdateValueUI();
}
protected override void OnReadOnlyStateChanged(bool isReadOnly)
{
if (_minField != null) _minField.isReadOnly = isReadOnly;
if (_maxField != null) _maxField.isReadOnly = isReadOnly;
}
#endregion
#region Event Handling
private void OnMinChanged(int newMin)
{
var newValue = new UTKIntRange(newMin, _value.Max);
if (!_value.Equals(newValue))
{
_value = newValue;
OnValueChanged?.Invoke(newValue);
if (_boundData != null && !_boundData.Value.Equals(newValue))
{
_boundData.Value = newValue;
}
}
}
private void OnMaxChanged(int newMax)
{
var newValue = new UTKIntRange(_value.Min, newMax);
if (!_value.Equals(newValue))
{
_value = newValue;
OnValueChanged?.Invoke(newValue);
if (_boundData != null && !_boundData.Value.Equals(newValue))
{
_boundData.Value = newValue;
}
}
}
#endregion
#region Value Update
private void UpdateValueUI()
{
if (_minField != null && _minField.Value != _value.Min)
{
_minField.SetValueWithoutNotify(_value.Min);
}
if (_maxField != null && _maxField.Value != _value.Max)
{
_maxField.SetValueWithoutNotify(_value.Max);
}
}
#endregion
#region Data Binding
public void Bind(IUTKPropertyItem data)
{
if (data is IUTKPropertyItem<UTKIntRange> rangeData)
{
Bind(rangeData);
}
else
{
Debug.LogWarning($"[UTKIntRangePropertyItemView] Cannot bind to non-IntRange data: {data.GetType().Name}");
}
}
public void Bind(IUTKPropertyItem<UTKIntRange> data)
{
Unbind();
_boundData = data;
Label = data.Name;
_value = data.Value;
IsReadOnly = data.IsReadOnly;
IsVisible = data.IsVisible;
TooltipText = data.Tooltip;
data.OnTypedValueChanged += OnDataValueChanged;
UpdateValueUI();
UpdateReadOnlyState();
}
public void Unbind()
{
if (_boundData != null)
{
_boundData.OnTypedValueChanged -= OnDataValueChanged;
_boundData = null;
}
}
private void OnDataValueChanged(IUTKPropertyItem<UTKIntRange> item, UTKIntRange oldValue, UTKIntRange newValue)
{
if (!_value.Equals(newValue))
{
_value = newValue;
UpdateValueUI();
}
}
#endregion
#region Dispose
protected override void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
UnregisterEvents();
Unbind();
OnValueChanged = null;
_minField = null;
_maxField = null;
}
base.Dispose(disposing);
}
#endregion
}
}