297 lines
8.4 KiB
C#
297 lines
8.4 KiB
C#
#nullable enable
|
|
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace UVC.UIToolkit
|
|
{
|
|
/// <summary>
|
|
/// FloatRange 속성 View 클래스입니다.
|
|
/// Min, Max 두 개의 UTKFloatField를 사용하여 실수 범위를 표시/편집합니다.
|
|
/// </summary>
|
|
[UxmlElement]
|
|
public partial class UTKFloatRangePropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<UTKFloatRange>
|
|
{
|
|
#region Fields
|
|
private UTKFloatField? _minField;
|
|
private UTKFloatField? _maxField;
|
|
|
|
private UTKFloatRange _value;
|
|
private IUTKPropertyItem<UTKFloatRange>? _boundData;
|
|
#endregion
|
|
|
|
#region Properties
|
|
protected override string ViewTypeName => "UTKFloatRangePropertyItemView";
|
|
|
|
/// <summary>현재 값</summary>
|
|
public UTKFloatRange 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<UTKFloatRange>? OnValueChanged;
|
|
#endregion
|
|
|
|
#region Constructor
|
|
public UTKFloatRangePropertyItemView() : base()
|
|
{
|
|
InitializeUI();
|
|
}
|
|
|
|
public UTKFloatRangePropertyItemView(string label, UTKFloatRange value = default) : base()
|
|
{
|
|
_value = value;
|
|
Label = label;
|
|
InitializeUI();
|
|
}
|
|
|
|
public UTKFloatRangePropertyItemView(string label, float min, float max) : base()
|
|
{
|
|
_value = new UTKFloatRange(min, max);
|
|
Label = label;
|
|
InitializeUI();
|
|
}
|
|
#endregion
|
|
|
|
#region Initialization
|
|
private void InitializeUI()
|
|
{
|
|
AddToClassList("utk-property-item-view");
|
|
AddToClassList("utk-property-item-view--float-range");
|
|
|
|
if (!CreateUIFromUxml())
|
|
{
|
|
CreateUIFallback();
|
|
}
|
|
|
|
// UXML에서 요소 가져오기
|
|
QueryUIElements();
|
|
|
|
// 이벤트 등록
|
|
RegisterEvents();
|
|
|
|
UpdateValueUI();
|
|
UpdateReadOnlyState();
|
|
}
|
|
|
|
private void QueryUIElements()
|
|
{
|
|
_minField = this.Q<UTKFloatField>("min-field");
|
|
_maxField = this.Q<UTKFloatField>("max-field");
|
|
|
|
// Fallback: UXML에서 못 찾으면 생성
|
|
if (_valueContainer != null)
|
|
{
|
|
_valueContainer.style.flexDirection = FlexDirection.Row;
|
|
|
|
if (_minField == null)
|
|
{
|
|
_minField = new UTKFloatField { 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 UTKFloatField { 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(float newMin)
|
|
{
|
|
var newValue = new UTKFloatRange(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(float newMax)
|
|
{
|
|
var newValue = new UTKFloatRange(_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 && !Mathf.Approximately(_minField.Value, _value.Min))
|
|
{
|
|
_minField.SetValueWithoutNotify(_value.Min);
|
|
}
|
|
|
|
if (_maxField != null && !Mathf.Approximately(_maxField.Value, _value.Max))
|
|
{
|
|
_maxField.SetValueWithoutNotify(_value.Max);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Data Binding
|
|
public void Bind(IUTKPropertyItem data)
|
|
{
|
|
if (data is IUTKPropertyItem<UTKFloatRange> rangeData)
|
|
{
|
|
Bind(rangeData);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"[UTKFloatRangePropertyItemView] Cannot bind to non-FloatRange data: {data.GetType().Name}");
|
|
}
|
|
}
|
|
|
|
public void Bind(IUTKPropertyItem<UTKFloatRange> 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<UTKFloatRange> item, UTKFloatRange oldValue, UTKFloatRange 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
|
|
}
|
|
}
|