225 lines
9.3 KiB
C#
225 lines
9.3 KiB
C#
using System;
|
|
using System.Globalization;
|
|
using TMPro;
|
|
using Unity.VisualScripting;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using UVC.Extention;
|
|
using UVC.UI.Tooltip;
|
|
|
|
namespace UVC.UI.Window.PropertyWindow.UI
|
|
{
|
|
/// <summary>
|
|
/// Int, Float RangeProperty를 위한 UI를 제어하는 스크립트입니다.
|
|
/// 범위의 시작(Min)과 끝(Max) 값을 별도의 InputField로 관리합니다.
|
|
/// </summary>
|
|
[RequireComponent(typeof(LayoutElement))]
|
|
public class NumberRangePropertyUI : MonoBehaviour, IPropertyUI
|
|
{
|
|
[Header("UI Components")]
|
|
[SerializeField]
|
|
private TextMeshProUGUI _nameLabel; // 속성 전체 이름
|
|
|
|
[SerializeField]
|
|
private TextMeshProUGUI _descriptionLabel;
|
|
|
|
[SerializeField]
|
|
private TMP_InputField _minInputField; // 최소값 입력 필드
|
|
|
|
[SerializeField]
|
|
private TMP_InputField _maxInputField; // 최대값 입력 필드
|
|
|
|
private IPropertyItem _propertyItem;
|
|
private PropertyWindow _controller;
|
|
|
|
/// <summary>
|
|
/// PropertyView에 의해 호출되어 UI를 초기화하고 데이터를 설정합니다.
|
|
/// </summary>
|
|
public void Setup(IPropertyItem item, PropertyWindow controller)
|
|
{
|
|
if (!(item is IntRangeProperty intRangeItem) && !(item is FloatRangeProperty floatRange))
|
|
{
|
|
Debug.LogError($"IntRangePropertyUI에 잘못된 타입의 PropertyItem이 전달되었습니다. {item.GetType()}");
|
|
return;
|
|
}
|
|
|
|
_propertyItem = item;
|
|
_controller = controller;
|
|
|
|
// --- 데이터 바인딩 ---
|
|
_nameLabel.text = _propertyItem.Name;
|
|
|
|
// 툴팁 설정 (TooltipHandler 컴포넌트가 있다면 연동)
|
|
TooltipHandler tooltipHandler = _nameLabel.GetComponent<TooltipHandler>();
|
|
if (tooltipHandler != null && !_propertyItem.Tooltip.IsNullOrEmpty())
|
|
{
|
|
tooltipHandler.Tooltip = _propertyItem.Tooltip;
|
|
}
|
|
|
|
if (_propertyItem.Description.IsNullOrEmpty())
|
|
{
|
|
_descriptionLabel.gameObject.SetActive(false);
|
|
}
|
|
else
|
|
{
|
|
_descriptionLabel.gameObject.SetActive(true);
|
|
_descriptionLabel.text = _propertyItem.Description;
|
|
}
|
|
|
|
if (item.PropertyType == PropertyType.IntRange)
|
|
{
|
|
_minInputField.contentType = TMP_InputField.ContentType.IntegerNumber;
|
|
_maxInputField.contentType = TMP_InputField.ContentType.IntegerNumber;
|
|
|
|
// 초기 값 설정
|
|
_minInputField.text = (_propertyItem as IntRangeProperty).Value.Item1.ToString(CultureInfo.InvariantCulture);
|
|
_maxInputField.text = (_propertyItem as IntRangeProperty).Value.Item2.ToString(CultureInfo.InvariantCulture);
|
|
}
|
|
else if (item.PropertyType == PropertyType.FloatRange)
|
|
{
|
|
_minInputField.contentType = TMP_InputField.ContentType.DecimalNumber;
|
|
_maxInputField.contentType = TMP_InputField.ContentType.DecimalNumber;
|
|
|
|
// 초기 값 설정
|
|
_minInputField.text = (_propertyItem as FloatRangeProperty).Value.Item1.ToString(CultureInfo.InvariantCulture);
|
|
_maxInputField.text = (_propertyItem as FloatRangeProperty).Value.Item2.ToString(CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
|
|
|
|
// 읽기 전용 상태 설정
|
|
bool isReadOnly = _propertyItem.IsReadOnly;
|
|
_minInputField.interactable = !isReadOnly;
|
|
_maxInputField.interactable = !isReadOnly;
|
|
|
|
// --- 이벤트 리스너 등록 ---
|
|
_minInputField.onEndEdit.RemoveAllListeners();
|
|
_maxInputField.onEndEdit.RemoveAllListeners();
|
|
_minInputField.onEndEdit.AddListener(OnValueSubmitted);
|
|
_maxInputField.onEndEdit.AddListener(OnValueSubmitted);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 사용자가 Min 또는 Max 입력 필드의 수정을 완료했을 때 호출됩니다.
|
|
/// </summary>
|
|
private void OnValueSubmitted(string input)
|
|
{
|
|
// 입력 필드의 값을 파싱합니다.
|
|
if (_propertyItem.PropertyType == PropertyType.IntRange)
|
|
{
|
|
OnValueSubmittedInt(input);
|
|
}
|
|
else if (_propertyItem.PropertyType == PropertyType.FloatRange)
|
|
{
|
|
OnValueSubmittedFloat(input);
|
|
}
|
|
}
|
|
|
|
private void OnValueSubmittedInt(string input)
|
|
{
|
|
IntRangeProperty propertyItem = _propertyItem as IntRangeProperty;
|
|
if (propertyItem == null) return;
|
|
|
|
int.TryParse(_minInputField.text, out int newMin);
|
|
int.TryParse(_maxInputField.text, out int newMax);
|
|
// 시작 값이 끝 값보다 크지 않도록 보정합니다.
|
|
if (newMin > newMax)
|
|
{
|
|
newMin = newMax;
|
|
}
|
|
|
|
// 이전 값을 저장합니다.
|
|
var oldValue = new Vector2Int(propertyItem.Value.Item1, propertyItem.Value.Item2);
|
|
var newValue = new Vector2Int(newMin, newMax);
|
|
|
|
// 값이 변경되었는지 확인합니다.
|
|
if (oldValue == newValue)
|
|
{
|
|
// 값이 변경되지 않았으면 UI만 원래 값으로 복원하고 종료합니다.
|
|
_minInputField.text = propertyItem.Value.Item1.ToString(CultureInfo.InvariantCulture);
|
|
_maxInputField.text = propertyItem.Value.Item2.ToString(CultureInfo.InvariantCulture);
|
|
return;
|
|
}
|
|
|
|
// 변경 이벤트를 발생시켜 다른 부분에 알립니다.
|
|
_controller.UpdatePropertyValue(_propertyItem.Id, _propertyItem.PropertyType, new Tuple<int, int>(newMin, newMax));
|
|
|
|
// 보정된 값으로 UI를 다시 업데이트합니다.
|
|
_minInputField.text = newMin.ToString(CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
private void OnValueSubmittedFloat(string input)
|
|
{
|
|
FloatRangeProperty propertyItem = _propertyItem as FloatRangeProperty;
|
|
if (propertyItem == null) return;
|
|
|
|
float.TryParse(_minInputField.text, out float newMin);
|
|
float.TryParse(_maxInputField.text, out float newMax);
|
|
// 시작 값이 끝 값보다 크지 않도록 보정합니다.
|
|
if (newMin > newMax)
|
|
{
|
|
newMin = newMax;
|
|
}
|
|
|
|
// 이전 값을 저장합니다.
|
|
var oldValue = new Vector2(propertyItem.Value.Item1, propertyItem.Value.Item2);
|
|
var newValue = new Vector2(newMin, newMax);
|
|
|
|
// 값이 변경되었는지 확인합니다.
|
|
if (oldValue == newValue)
|
|
{
|
|
// 값이 변경되지 않았으면 UI만 원래 값으로 복원하고 종료합니다.
|
|
_minInputField.text = propertyItem.Value.Item1.ToString(CultureInfo.InvariantCulture);
|
|
_maxInputField.text = propertyItem.Value.Item2.ToString(CultureInfo.InvariantCulture);
|
|
return;
|
|
}
|
|
|
|
// 변경 이벤트를 발생시켜 다른 부분에 알립니다.
|
|
_controller.UpdatePropertyValue(_propertyItem.Id, _propertyItem.PropertyType, new Tuple<float, float>(newMin, newMax));
|
|
|
|
// 보정된 값으로 UI를 다시 업데이트합니다.
|
|
_minInputField.text = newMin.ToString(CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
/// <summary>
|
|
/// UI의 읽기 전용 상태를 설정합니다.
|
|
/// </summary>
|
|
/// <param name="isReadOnly">읽기 전용 여부 (true: 비활성화, false: 활성화)</param>
|
|
public void SetReadOnly(bool isReadOnly)
|
|
{
|
|
if (_propertyItem != null)
|
|
{
|
|
_propertyItem.IsReadOnly = isReadOnly;
|
|
}
|
|
if (_minInputField != null) _minInputField.interactable = !isReadOnly;
|
|
if (_maxInputField != null) _maxInputField.interactable = !isReadOnly;
|
|
}
|
|
|
|
/// <summary>
|
|
/// UI에 표시되는 값을 업데이트합니다.
|
|
/// </summary>
|
|
/// <param name="value">새로운 값</param>
|
|
public void UpdateValue(object value)
|
|
{
|
|
if (value is Tuple<int, int> intRange)
|
|
{
|
|
if (_minInputField != null) _minInputField.SetTextWithoutNotify(intRange.Item1.ToString(CultureInfo.InvariantCulture));
|
|
if (_maxInputField != null) _maxInputField.SetTextWithoutNotify(intRange.Item2.ToString(CultureInfo.InvariantCulture));
|
|
}
|
|
else if (value is Tuple<float, float> floatRange)
|
|
{
|
|
if (_minInputField != null) _minInputField.SetTextWithoutNotify(floatRange.Item1.ToString(CultureInfo.InvariantCulture));
|
|
if (_maxInputField != null) _maxInputField.SetTextWithoutNotify(floatRange.Item2.ToString(CultureInfo.InvariantCulture));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이 UI 오브젝트가 파괴될 때 Unity에 의해 호출됩니다.
|
|
/// </summary>
|
|
private void OnDestroy()
|
|
{
|
|
if (_minInputField != null) _minInputField.onEndEdit.RemoveAllListeners();
|
|
if (_maxInputField != null) _maxInputField.onEndEdit.RemoveAllListeners();
|
|
}
|
|
}
|
|
} |