Files
XRLib/Assets/Scripts/UVC/UIToolkit/Property/Items/UTKFloatPropertyItem.cs
2026-01-20 20:18:47 +09:00

207 lines
6.0 KiB
C#

#nullable enable
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit
{
/// <summary>
/// 실수 속성 아이템
/// FloatField 또는 Slider 사용
/// </summary>
public class UTKFloatPropertyItem : UTKPropertyItemBase<float>
{
#region Fields
private FloatField? _floatField;
private Slider? _slider;
private bool _useSlider;
private float _minValue;
private float _maxValue = 1f;
#endregion
#region Properties
public override UTKPropertyType PropertyType => UTKPropertyType.Float;
/// <summary>슬라이더 사용 여부</summary>
public bool UseSlider
{
get => _useSlider;
set => _useSlider = value;
}
/// <summary>최소값 (슬라이더 모드)</summary>
public float MinValue
{
get => _minValue;
set
{
_minValue = value;
if (_slider != null)
{
_slider.lowValue = value;
}
}
}
/// <summary>최대값 (슬라이더 모드)</summary>
public float MaxValue
{
get => _maxValue;
set
{
_maxValue = value;
if (_slider != null)
{
_slider.highValue = value;
}
}
}
#endregion
#region Constructor
public UTKFloatPropertyItem(string id, string name, float initialValue = 0f)
: base(id, name, initialValue)
{
}
public UTKFloatPropertyItem(string id, string name, float initialValue, float minValue, float maxValue, bool useSlider = true)
: base(id, name, initialValue)
{
_minValue = minValue;
_maxValue = maxValue;
_useSlider = useSlider;
}
#endregion
#region Override Methods
public override VisualElement CreateUI()
{
var container = CreateContainer();
if (_useSlider)
{
container.AddToClassList("utk-property-item--slider");
}
var label = CreateNameLabel();
container.Add(label);
var valueContainer = new VisualElement();
valueContainer.AddToClassList("utk-property-item__value");
if (_useSlider)
{
_slider = new Slider(_minValue, _maxValue);
_slider.name = "slider-field";
_slider.value = Value;
_slider.AddToClassList("utk-property-item__slider");
valueContainer.Add(_slider);
_floatField = new FloatField();
_floatField.name = "value-field";
_floatField.value = Value;
_floatField.AddToClassList("utk-property-item__number-field");
valueContainer.Add(_floatField);
}
else
{
_floatField = new FloatField();
_floatField.name = "value-field";
_floatField.value = Value;
valueContainer.Add(_floatField);
}
container.Add(valueContainer);
return container;
}
public override void BindUI(VisualElement element)
{
base.BindUI(element);
_floatField = element.Q<FloatField>("value-field");
_slider = element.Q<Slider>("slider-field");
if (_floatField != null)
{
_floatField.value = Value;
_floatField.SetEnabled(!IsReadOnly);
_floatField.RegisterValueChangedCallback(OnFloatFieldChanged);
}
if (_slider != null)
{
_slider.lowValue = _minValue;
_slider.highValue = _maxValue;
_slider.value = Value;
_slider.SetEnabled(!IsReadOnly);
_slider.RegisterValueChangedCallback(OnSliderChanged);
}
}
public override void UnbindUI(VisualElement element)
{
if (_floatField != null)
{
_floatField.UnregisterValueChangedCallback(OnFloatFieldChanged);
_floatField = null;
}
if (_slider != null)
{
_slider.UnregisterValueChangedCallback(OnSliderChanged);
_slider = null;
}
base.UnbindUI(element);
}
public override void RefreshUI()
{
if (_floatField != null && !Mathf.Approximately(_floatField.value, Value))
{
_floatField.SetValueWithoutNotify(Value);
}
if (_slider != null && !Mathf.Approximately(_slider.value, Value))
{
_slider.SetValueWithoutNotify(Value);
}
}
protected override void UpdateReadOnlyState()
{
base.UpdateReadOnlyState();
_floatField?.SetEnabled(!IsReadOnly);
_slider?.SetEnabled(!IsReadOnly);
}
#endregion
#region Private Methods
private void OnFloatFieldChanged(ChangeEvent<float> evt)
{
float clampedValue = _useSlider ? Mathf.Clamp(evt.newValue, _minValue, _maxValue) : evt.newValue;
if (_slider != null && !Mathf.Approximately(_slider.value, clampedValue))
{
_slider.SetValueWithoutNotify(clampedValue);
}
DebounceValueChange(clampedValue, 100).Forget();
}
private void OnSliderChanged(ChangeEvent<float> evt)
{
if (_floatField != null && !Mathf.Approximately(_floatField.value, evt.newValue))
{
_floatField.SetValueWithoutNotify(evt.newValue);
}
// 슬라이더는 빠른 반응이 필요하므로 짧은 디바운스
DebounceValueChange(evt.newValue, 50).Forget();
}
#endregion
}
}