403 lines
12 KiB
C#
403 lines
12 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에서 사용
|
|
/// <utk:UTKStringPropertyItemView label="이름" value="홍길동" is-multiline="false" />
|
|
/// </code>
|
|
/// </summary>
|
|
[UxmlElement]
|
|
public partial class UTKStringPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<string>
|
|
{
|
|
#region Fields
|
|
private UTKInputField? _inputField;
|
|
private UTKButton? _actionButton;
|
|
private Action? _actionButtonClickHandler; // 이벤트 핸들러 참조 보관
|
|
|
|
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;
|
|
|
|
/// <summary>액션 버튼 클릭 이벤트 (actionName 전달)</summary>
|
|
public event Action<string>? OnActionButtonClicked;
|
|
#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 Action Button
|
|
private void CreateActionButton(UTKButtonItem buttonItem)
|
|
{
|
|
// 기존 버튼 제거
|
|
RemoveActionButton();
|
|
|
|
// 새 버튼 생성
|
|
_actionButton = new UTKButton
|
|
{
|
|
name = buttonItem.Name,
|
|
Text = buttonItem.Text,
|
|
Icon = buttonItem.Icon,
|
|
IconSize = buttonItem.IconSize,
|
|
Variant = buttonItem.Variant,
|
|
Size = buttonItem.Size,
|
|
IconOnly = buttonItem.IconOnly
|
|
};
|
|
if(buttonItem.IconOnly == false){
|
|
_actionButton.AddToClassList("utk-property-item-view__action-button");
|
|
}
|
|
else
|
|
{
|
|
_actionButton.AddToClassList("utk-property-item-view__action-button-icon");
|
|
}
|
|
|
|
if (buttonItem.BackgroundColor.HasValue)
|
|
{
|
|
_actionButton.BackgroundColor = buttonItem.BackgroundColor;
|
|
}
|
|
|
|
if (buttonItem.BorderWidth >= 0)
|
|
{
|
|
_actionButton.BorderWidth = buttonItem.BorderWidth;
|
|
}
|
|
|
|
// 버튼 클릭 이벤트 등록 (핸들러 참조 보관)
|
|
_actionButtonClickHandler = () => OnActionButtonClicked?.Invoke(buttonItem.ActionName);
|
|
_actionButton.OnClicked += _actionButtonClickHandler;
|
|
|
|
// value-container에 추가 (InputField 다음)
|
|
_valueContainer?.Add(_actionButton);
|
|
}
|
|
|
|
private void RemoveActionButton()
|
|
{
|
|
if (_actionButton != null)
|
|
{
|
|
// 보관된 핸들러로 이벤트 해제
|
|
if (_actionButtonClickHandler != null)
|
|
{
|
|
_actionButton.OnClicked -= _actionButtonClickHandler;
|
|
_actionButtonClickHandler = null;
|
|
}
|
|
|
|
_actionButton.RemoveFromHierarchy();
|
|
_actionButton.Dispose();
|
|
_actionButton = null;
|
|
}
|
|
}
|
|
#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;
|
|
BindBase(data);
|
|
|
|
Label = data.Name;
|
|
_value = data.Value ?? string.Empty;
|
|
IsVisible = data.IsVisible;
|
|
TooltipText = data.Tooltip;
|
|
ShowLabel = data.ShowLabel;
|
|
|
|
if (data is UTKStringPropertyItem stringItem)
|
|
{
|
|
_isMultiline = stringItem.IsMultiline;
|
|
_maxLength = stringItem.MaxLength;
|
|
if (_inputField != null)
|
|
{
|
|
_inputField.multiline = _isMultiline;
|
|
_inputField.maxLength = _maxLength;
|
|
}
|
|
|
|
// ActionButton이 있으면 버튼 생성
|
|
if (stringItem.ActionButton != null && _valueContainer != null)
|
|
{
|
|
CreateActionButton(stringItem.ActionButton);
|
|
}
|
|
else
|
|
{
|
|
RemoveActionButton();
|
|
}
|
|
}
|
|
|
|
data.OnTypedValueChanged += OnDataValueChanged;
|
|
|
|
UpdateValueUI();
|
|
IsReadOnly = data.IsReadOnly;
|
|
}
|
|
|
|
public void Unbind()
|
|
{
|
|
if (_boundData != null)
|
|
{
|
|
_boundData.OnTypedValueChanged -= OnDataValueChanged;
|
|
UnbindBase();
|
|
_boundData = null;
|
|
}
|
|
|
|
// ActionButton 제거
|
|
RemoveActionButton();
|
|
}
|
|
|
|
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();
|
|
|
|
// UTKInputField Dispose 호출
|
|
if (_inputField is IDisposable disposableField)
|
|
{
|
|
disposableField.Dispose();
|
|
}
|
|
|
|
OnValueChanged = null;
|
|
OnActionButtonClicked = null;
|
|
_inputField = null;
|
|
}
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|