#nullable enable
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit.Property
{
///
/// UTKPropertyList를 감싸는 윈도우 래퍼
/// 헤더, 타이틀, 닫기 버튼 등 윈도우 프레임 제공
///
[UxmlElement]
public partial class UTKPropertyWindow : VisualElement, IDisposable
{
#region Constants
private const string USS_PATH = "UIToolkit/Property/UTKPropertyWindow";
#endregion
#region Fields
private bool _disposed;
private UTKPropertyList? _propertyList;
private VisualElement? _header;
private Label? _titleLabel;
private Button? _closeButton;
private string _title = "Properties";
private bool _showCloseButton = true;
private bool _isDragging;
private Vector2 _dragStartPosition;
private Vector2 _dragStartMousePosition;
#endregion
#region Properties
/// 윈도우 타이틀
public string Title
{
get => _title;
set
{
_title = value;
if (_titleLabel != null)
{
_titleLabel.text = value;
}
}
}
/// 닫기 버튼 표시 여부
public bool ShowCloseButton
{
get => _showCloseButton;
set
{
_showCloseButton = value;
if (_closeButton != null)
{
_closeButton.style.display = value ? DisplayStyle.Flex : DisplayStyle.None;
}
}
}
/// 내부 PropertyList 접근
public UTKPropertyList PropertyList => _propertyList ??= new UTKPropertyList();
#endregion
#region Events
/// 닫기 버튼 클릭 이벤트
public event Action? OnCloseClicked;
/// 속성 값 변경 이벤트 (PropertyList 위임)
public event Action? OnPropertyValueChanged
{
add => PropertyList.OnPropertyValueChanged += value;
remove => PropertyList.OnPropertyValueChanged -= value;
}
/// 그룹 펼침/접힘 이벤트 (PropertyList 위임)
public event Action? OnGroupExpandedChanged
{
add => PropertyList.OnGroupExpandedChanged += value;
remove => PropertyList.OnGroupExpandedChanged -= value;
}
/// 속성 클릭 이벤트 (PropertyList 위임)
public event Action? OnPropertyClicked
{
add => PropertyList.OnPropertyClicked += value;
remove => PropertyList.OnPropertyClicked -= value;
}
#endregion
#region Constructor
public UTKPropertyWindow()
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
var styleSheet = Resources.Load(USS_PATH);
if (styleSheet != null)
{
styleSheets.Add(styleSheet);
}
CreateUI();
}
public UTKPropertyWindow(string title) : this()
{
Title = title;
}
#endregion
#region UI Creation
private void CreateUI()
{
AddToClassList("utk-property-window");
// 헤더
_header = new VisualElement();
_header.name = "header";
_header.AddToClassList("utk-property-window__header");
_titleLabel = new Label(_title);
_titleLabel.name = "title";
_titleLabel.AddToClassList("utk-property-window__title");
_header.Add(_titleLabel);
_closeButton = new Button { text = "✕" };
_closeButton.name = "close-btn";
_closeButton.AddToClassList("utk-property-window__close-btn");
_closeButton.clicked += () => OnCloseClicked?.Invoke();
_header.Add(_closeButton);
Add(_header);
// 드래그 이벤트
_header.RegisterCallback(OnHeaderPointerDown);
_header.RegisterCallback(OnHeaderPointerMove);
_header.RegisterCallback(OnHeaderPointerUp);
// PropertyList
_propertyList = new UTKPropertyList();
_propertyList.AddToClassList("utk-property-window__content");
Add(_propertyList);
}
#endregion
#region Public Methods - PropertyList 위임
public void LoadProperties(List items) => PropertyList.LoadProperties(items);
public void LoadGroupedProperties(List groups) => PropertyList.LoadGroupedProperties(groups);
public void LoadMixedProperties(List entries) => PropertyList.LoadMixedProperties(entries);
public void AddGroup(IUTKPropertyGroup group) => PropertyList.AddGroup(group);
public void RemoveGroup(string groupId) => PropertyList.RemoveGroup(groupId);
public IUTKPropertyGroup? GetGroup(string groupId) => PropertyList.GetGroup(groupId);
public void SetGroupExpanded(string groupId, bool expanded) => PropertyList.SetGroupExpanded(groupId, expanded);
public void ToggleGroupExpanded(string groupId) => PropertyList.ToggleGroupExpanded(groupId);
public void AddProperty(IUTKPropertyItem item) => PropertyList.AddProperty(item);
public void AddPropertyToGroup(string groupId, IUTKPropertyItem item) => PropertyList.AddPropertyToGroup(groupId, item);
public void RemoveProperty(string itemId) => PropertyList.RemoveProperty(itemId);
public IUTKPropertyItem? GetProperty(string itemId) => PropertyList.GetProperty(itemId);
public void UpdatePropertyValue(string propertyId, object newValue) => PropertyList.UpdatePropertyValue(propertyId, newValue);
public void SetPropertyValue(string propertyId, object value) => PropertyList.SetPropertyValue(propertyId, value);
public void SetPropertyVisibility(string propertyId, bool visible) => PropertyList.SetPropertyVisibility(propertyId, visible);
public void SetGroupVisibility(string groupId, bool visible) => PropertyList.SetGroupVisibility(groupId, visible);
public void SetPropertyReadOnly(string propertyId, bool isReadOnly) => PropertyList.SetPropertyReadOnly(propertyId, isReadOnly);
public void SetGroupReadOnly(string groupId, bool isReadOnly) => PropertyList.SetGroupReadOnly(groupId, isReadOnly);
public void Clear() => PropertyList.Clear();
public void Refresh() => PropertyList.Refresh();
#endregion
#region Public Methods - Window
public void Show()
{
style.display = DisplayStyle.Flex;
}
public void Hide()
{
style.display = DisplayStyle.None;
}
public void SetPosition(float x, float y)
{
style.left = x;
style.top = y;
}
public void SetSize(float width, float height)
{
style.width = width;
style.height = height;
}
public void CenterOnScreen()
{
schedule.Execute(() =>
{
var parent = this.parent;
if (parent == null) return;
float parentWidth = parent.resolvedStyle.width;
float parentHeight = parent.resolvedStyle.height;
float selfWidth = resolvedStyle.width;
float selfHeight = resolvedStyle.height;
style.left = (parentWidth - selfWidth) / 2;
style.top = (parentHeight - selfHeight) / 2;
});
}
#endregion
#region Dragging
private void OnHeaderPointerDown(PointerDownEvent evt)
{
if (evt.button != 0) return;
_isDragging = true;
_dragStartPosition = new Vector2(resolvedStyle.left, resolvedStyle.top);
_dragStartMousePosition = evt.position;
_header?.CapturePointer(evt.pointerId);
}
private void OnHeaderPointerMove(PointerMoveEvent evt)
{
if (!_isDragging) return;
Vector2 delta = (Vector2)evt.position - _dragStartMousePosition;
style.left = _dragStartPosition.x + delta.x;
style.top = _dragStartPosition.y + delta.y;
}
private void OnHeaderPointerUp(PointerUpEvent evt)
{
if (!_isDragging) return;
_isDragging = false;
_header?.ReleasePointer(evt.pointerId);
}
#endregion
#region IDisposable
public void Dispose()
{
if (_disposed) return;
_disposed = true;
// 드래그 이벤트 해제
if (_header != null)
{
_header.UnregisterCallback(OnHeaderPointerDown);
_header.UnregisterCallback(OnHeaderPointerMove);
_header.UnregisterCallback(OnHeaderPointerUp);
}
// PropertyList 정리
_propertyList?.Dispose();
_propertyList = null;
// 이벤트 정리
OnCloseClicked = null;
// UI 참조 정리
_header = null;
_titleLabel = null;
_closeButton = null;
}
#endregion
}
}