Files
XRLib/Assets/Scripts/UVC/UIToolkit/Property/Views/UTKStringPropertyItemView.cs
2026-02-04 20:31:52 +09:00

315 lines
8.6 KiB
C#

#nullable enable
using System;
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit
{
/// <summary>
/// String 속성 View 클래스입니다.
/// UTKInputField를 사용하여 문자열 값을 표시/편집합니다.
///
/// <para><b>사용법 (단독 사용):</b></para>
/// <code>
/// // C# 코드에서 생성
/// var view = new UTKStringPropertyItemView();
/// view.Label = "이름";
/// view.Value = "홍길동";
/// view.IsMultiline = false;
/// parent.Add(view);
///
/// // UXML에서 사용
/// &lt;utk:UTKStringPropertyItemView label="이름" value="홍길동" is-multiline="false" /&gt;
/// </code>
/// </summary>
[UxmlElement]
public partial class UTKStringPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<string>
{
#region Fields
private UTKInputField? _inputField;
private string _value = string.Empty;
private bool _isMultiline = false;
private int _maxLength = -1;
private IUTKPropertyItem<string>? _boundData;
#endregion
#region Properties
protected override string ViewTypeName => "UTKStringPropertyItemView";
/// <summary>현재 값</summary>
[UxmlAttribute("value")]
public string Value
{
get => _value;
set
{
var newValue = value ?? string.Empty;
if (_value != newValue)
{
_value = newValue;
UpdateValueUI();
OnValueChanged?.Invoke(newValue);
if (_boundData != null && _boundData.Value != newValue)
{
_boundData.Value = newValue;
}
}
}
}
/// <summary>멀티라인 모드 여부</summary>
[UxmlAttribute("is-multiline")]
public bool IsMultiline
{
get => _isMultiline;
set
{
_isMultiline = value;
if (_inputField != null)
{
_inputField.multiline = value;
}
}
}
/// <summary>최대 문자 길이 (-1 = 무제한)</summary>
[UxmlAttribute("max-length")]
public int MaxLength
{
get => _maxLength;
set
{
_maxLength = value;
if (_inputField != null)
{
_inputField.maxLength = value;
}
}
}
#endregion
#region Events
public event Action<string>? OnValueChanged;
#endregion
#region Constructor
public UTKStringPropertyItemView() : base()
{
InitializeUI();
}
public UTKStringPropertyItemView(UTKStringPropertyItem item) : base()
{
_value = item.Value ?? string.Empty;
_isMultiline = item.IsMultiline;
_maxLength = item.MaxLength;
Label = item.Name;
_isReadOnly = item.IsReadOnly;
InitializeUI();
Bind(item);
}
public UTKStringPropertyItemView(string label, string value = "", bool isMultiline = false, int maxLength = -1, bool isReadOnly = false) : base()
{
_value = value ?? string.Empty;
_isMultiline = isMultiline;
_maxLength = maxLength;
Label = label;
_isReadOnly = isReadOnly;
InitializeUI();
}
#endregion
#region Initialization
private void InitializeUI()
{
AddToClassList("utk-property-item-view");
AddToClassList("utk-property-item-view--string");
if (!CreateUIFromUxml())
{
CreateUIFallback();
}
// UXML에서 요소 가져오기
QueryUIElements();
// 이벤트 등록
RegisterEvents();
UpdateValueUI();
UpdateReadOnlyState();
}
private void QueryUIElements()
{
_inputField = this.Q<UTKInputField>("value-field");
// Fallback: UXML에서 못 찾으면 생성
if (_valueContainer != null && _inputField == null)
{
_inputField = new UTKInputField { name = "value-field" };
_inputField.AddToClassList("utk-property-item-view__field");
_valueContainer.Add(_inputField);
}
// 초기 값 설정
if (_inputField != null)
{
_inputField.SetValue(_value, false);
_inputField.multiline = _isMultiline;
_inputField.isReadOnly = IsReadOnly;
if (_maxLength >= 0)
{
_inputField.maxLength = _maxLength;
}
}
}
private void RegisterEvents()
{
if (_inputField != null)
{
_inputField.OnValueChanged += OnInputChanged;
}
}
private void UnregisterEvents()
{
if (_inputField != null)
{
_inputField.OnValueChanged -= OnInputChanged;
}
}
#endregion
#region Override Methods
protected override void CreateValueUI(VisualElement container)
{
// UXML/QueryUIElements 기반으로 생성하므로 여기서는 생성하지 않음
}
public override void RefreshUI()
{
UpdateValueUI();
}
protected override void OnReadOnlyStateChanged(bool isReadOnly)
{
if (_inputField != null)
{
_inputField.isReadOnly = isReadOnly;
}
}
#endregion
#region Event Handling
private void OnInputChanged(string newValue)
{
if (_value != newValue)
{
_value = newValue;
OnValueChanged?.Invoke(newValue);
if (_boundData != null && _boundData.Value != newValue)
{
_boundData.Value = newValue;
}
}
}
#endregion
#region Value Update
private void UpdateValueUI()
{
if (_inputField != null && _inputField.Value != _value)
{
_inputField.SetValue(_value, false);
}
}
#endregion
#region Data Binding
public void Bind(IUTKPropertyItem data)
{
if (data is IUTKPropertyItem<string> stringData)
{
Bind(stringData);
}
else
{
Debug.LogWarning($"[UTKStringPropertyItemView] Cannot bind to non-string data: {data.GetType().Name}");
}
}
public void Bind(IUTKPropertyItem<string> data)
{
Unbind();
_boundData = data;
Label = data.Name;
_value = data.Value ?? string.Empty;
IsReadOnly = data.IsReadOnly;
IsVisible = data.IsVisible;
TooltipText = data.Tooltip;
if (data is UTKStringPropertyItem stringItem)
{
_isMultiline = stringItem.IsMultiline;
_maxLength = stringItem.MaxLength;
if (_inputField != null)
{
_inputField.multiline = _isMultiline;
_inputField.maxLength = _maxLength;
}
}
data.OnTypedValueChanged += OnDataValueChanged;
UpdateValueUI();
UpdateReadOnlyState();
}
public void Unbind()
{
if (_boundData != null)
{
_boundData.OnTypedValueChanged -= OnDataValueChanged;
_boundData = null;
}
}
private void OnDataValueChanged(IUTKPropertyItem<string> item, string oldValue, string newValue)
{
if (_value != newValue)
{
_value = newValue ?? string.Empty;
UpdateValueUI();
}
}
#endregion
#region Dispose
protected override void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
UnregisterEvents();
Unbind();
OnValueChanged = null;
_inputField = null;
}
base.Dispose(disposing);
}
#endregion
}
}